2
\$\begingroup\$

I implemented a card game using pygame. The game is a game of war and uses png files that I made in Microsoft Paint. I tried to keep the object classes as separated from the pygame module as possible. The idea of this is so I can use the CardClasses module with other graphical interfaces, and to reuse it on different games whose displays will be different.

When I go to rewrite this or something similar, the idea is to plan on using a Game class as opposed to the more function paradigm I wrote here. I learned that I can't upload the card image files here, so I will upload the code in hopes that someone will comb through the lines and give me some feedback. Looking at how to rewrite the docstrings, I feel as though they are weak and only give the idea of the functions or classes they represent.

CardClasses.py

"""Created: 3/30/2019
Objects to represent a playing Card, playing Deck, and Player
"""
from enum import Enum
from itertools import product
from random import shuffle
class ranks(Enum):
 TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE = range(2, 15)
class suits(Enum):
 CLUBS,DIAMONDS,HEARTS,SPADES = range(1, 5)
class Card(object):
 """Card object represents a standard playing card.
 The object attributes, suit and rank, are implemented as enums whose values determine the weight of the card
 """
 def __init__(self, suit, rank, in_deck = False, image = None):
 if rank in ranks and suit in suits:
 self.rank = rank
 self.suit = suit
 else:
 self.rank = None
 self.suit = None
 self.in_deck = in_deck
 self.image = image
 self.position_x, self.position_y = 0,0
 self.horizontal_demension = None
 self.vertical_demension = None
 def __str__(self):
 return str(self.rank.name) + " " + str(self.suit.name)
 def __eq__(self, other):
 return True if self.rank == other.rank and self.suit == other.suit else False
 def __gt__(self, other):
 """Tests suit precedence, if suits are equal then checks ranks precedence"""
 if self.suit == other.suit:
 if self.rank.value > other.rank.value:
 return True
 if self.suit.value > other.suit.value:
 return True
 return False
class Deck(object):
 """A deck is a collection of 52 Card objects
 Object attributes: cards, removed
 methods: draw(range = 1), deck_shuffle()
 """
 def __init__(self):
 self.cards = [Card(suit, rank, in_deck = True) for suit, rank in product(suits, ranks)]
 self.removed = []
 def __str__(self):
 return str([str(card) for card in self.cards])
 def draw(self, range = 1):
 """Draw card(s) by removing them from deck"""
 drawn_cards = self.cards[:range]
 for card in drawn_cards:
 card.in_deck = False
 del self.cards[:range]
 self.removed.append(drawn_cards)
 return drawn_cards
 def deck_shuffle(self):
 """Shuffles deck object in place"""
 shuffle(self.cards)
class Player(object):
 """Implementation of a player object
 Object attributes: name, hand, score, turn, card_selected
 methods: remove_from_hand(card)
 """
 def __init__(self, name, hand = None, score = 0, turn = False):
 self.name = name
 self.hand = hand
 self.score = score
 self.turn = turn
 self.selected_card = None
 def __str__(self):
 return str(self.name)
 def remove_from_hand(self, card):
 """Removes a card object from the players hand"""
 if card and card in self.hand:
 position = self.hand.index(card)
 del self.hand[position]
 return card
 return None

Game.py

"""3/31/2019
Implementation of game of war using pygame and CardClasses
"""
from CardClasses import *
import pygame
green = (0, 200, 50)
def show_hand(screen, player):
 """Displays all cards in hand of player on pygame display object"""
 x, y, space_between_cards = 5, 462, 5
 for card in player.hand:
 card.position_x, card.position_y = x, y
 screen.blit(card.image, (x, y))
 x += card.horizontal_demension + space_between_cards
def select_card(player, mouse_x, mouse_y):
 """Player selects a card to play"""
 if mouse_x:
 for card in player.hand:
 lower_x, upper_x = (card.position_x, card.position_x + card.horizontal_demension)
 lower_y, upper_y = (card.position_y, card.position_y + card.vertical_demension)
 if mouse_x > lower_x and mouse_x < upper_x:
 if mouse_y > lower_y and mouse_y < upper_y:
 player.selected_card = card
def load_card_images(player):
 "Loads image, and demensions to card objects"
 for card in player.hand:
 card.image = pygame.image.load("Cards/" + str(card) + ".png")
 width, hieght = card.image.get_size()
 card.horizontal_demension = width
 card.vertical_demension = hieght
def play_selected_card(screen, player):
 """Display card that is selected on pygame display object"""
 x = player.selected_card.position_x = 220
 y = player.selected_card.position_y
 screen.blit(player.selected_card.image, (x,y))
def show_winner(screen, player1, player2, my_font):
 """Display text stating game winner at end of game"""
 screen.fill(green)
 winner = str(player1) if player1.score > player2.score else str(player2)
 textsurface = my_font.render("The winner is: " + winner, False, (0, 0, 0))
 screen.blit(textsurface, (100, 270))
def update_selected_card_position(player, new_y_position):
 """Change the Y position of selected card to move card to played position"""
 if player.selected_card:
 player.selected_card.position_y = new_y_position
def evaluate(player1, player2):
 """determines who won round and updates their score"""
 round_winner = None
 if player1.selected_card and player2.selected_card:
 pygame.time.delay(1000)
 round_winner = player1 if player1.selected_card > player2.selected_card else player2
 round_winner.score += 1
 player1.selected_card, player2.selected_card = None, None
 return round_winner
def show_player_scores(screen, player1, player2):
 """Left corner is player 1 score, right corner is player 2 score"""
 font_size = 12
 my_font = pygame.font.SysFont('Times New Roman', font_size)
 textsurface1 = my_font.render("Player 1 score: " + str(player1.score), False, (0, 0, 0))
 textsurface2 = my_font.render("Player 2 score: " + str(player2.score), False, (0, 0, 0))
 screen.blit(textsurface1, (0,0))
 screen.blit(textsurface2, (470,0))
def flip_turns(player1, player2):
 """Negates Turn attributes of player1 and player2"""
 player1.turn = not player1.turn
 player2.turn = not player2.turn
def turn(player, mouse_x, mouse_y, new_y_position):
 """Player will select card using mouse_x, and mouse_y, card will be removed from hand and played"""
 select_card(player, mouse_x, mouse_y)
 player.remove_from_hand(player.selected_card)
 update_selected_card_position(player, new_y_position)
def winner_goes_first(winner, loser):
 """Sets the winner to the starter of the next round"""
 winner.turn = True
 loser.turn = False
def main():
 """GAME of war, each player is given a hand of 10 cards, on each turn a player will select a card to play,
 players cards will be compared and the player with the greater in value card will be assigned a point for round victory.
 When all cards in hand have been played game ends and winner is displayed
 """
 sc_width, sc_height = 555, 555
 selected_card_y_pos_player_1 = 330
 selected_card_y_pos_player_2 = 230
 font_size = 30
 delay_time_ms = 1000
 number_of_cards = 10
 turn_count = 1
 deck = Deck()
 deck.deck_shuffle()
 player1 = Player(input("Player 1 name: "), hand = deck.draw(number_of_cards), turn = True)
 player2 = Player(input("Player 2 name: "), hand = deck.draw(number_of_cards))
 pygame.init()
 screen = pygame.display.set_mode((sc_width, sc_height))
 load_card_images(player1)
 load_card_images(player2)
 pygame.font.init()
 my_font = pygame.font.SysFont('Times New Roman', font_size)
 """Main Game Loop"""
 game_is_running = True
 while game_is_running:
 screen.fill(green)
 mouse_x, mouse_y = None, None
 events = pygame.event.get()
 for event in events:
 if event.type == pygame.QUIT:
 game_is_running = False
 quit()
 if event.type == pygame.MOUSEBUTTONUP:
 mouse_x, mouse_y = pygame.mouse.get_pos()
 if player1.turn:
 show_hand(screen, player1)
 turn(player1, mouse_x, mouse_y, selected_card_y_pos_player_1)
 if player1.selected_card:
 flip_turns(player1, player2)
 else:
 show_hand(screen, player2)
 turn(player2, mouse_x, mouse_y, selected_card_y_pos_player_2)
 if player2.selected_card:
 flip_turns(player1, player2)
 if player1.selected_card:
 play_selected_card(screen, player1)
 if player2.selected_card:
 play_selected_card(screen, player2)
 show_player_scores(screen, player1, player2)
 pygame.display.update()
 winner = evaluate(player1,player2)
 if winner:
 if winner == player1:
 winner_goes_first(player1, player2)
 else:
 winner_goes_first(player2, player1)
 if not player1.hand and not player2.hand:
 show_winner(screen, player1, player2, my_font)
 pygame.display.update()
 pygame.time.delay(delay_time_ms)
 game_is_running = False
if __name__ == '__main__':
 main()
toolic
14.5k5 gold badges29 silver badges203 bronze badges
asked Apr 9, 2019 at 1:54
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

DRY

The mouse_x variable is repeated in this expression:

if mouse_x > lower_x and mouse_x < upper_x:

It can be simplified as:

if lower_x < mouse_x < upper_x:

Class

object is not needed:

class Deck(object):

and is often omitted:

class Card():

The flip_turns function could have been implemented as a method in the Player class since the logic is duplicated in flip_turns.

Spelling

The variable named hieght would be better as height.

Layout

Long lines like this in the docstring:

players cards will be compared and the player with the greater in value card will be assigned a point for round victory.

should be split into multiple lines for better readability:

players cards will be compared and the player with the
greater in value card will be assigned a point for round victory.

Naming

The PEP 8 style guide recommends upper case for constant names. For example:

green = (0, 200, 50)

would be:

GREEN = (0, 200, 50)

The same goes for these:

sc_width, sc_height = 555, 555
selected_card_y_pos_player_1 = 330
selected_card_y_pos_player_2 = 230
font_size = 30
delay_time_ms = 1000
number_of_cards = 10

The variable named range in the Deck class is the same name as a Python built-in function. This can be confusing. To eliminate the confusion, rename the variable as something like card_range. The first clue is that "range" has special coloring (syntax highlighting) in the question, as it does when I copy the code into my editor.

Unused

This variable is set:

turn_count = 1

but it is never used otherwise. It should be deleted.

answered Jun 13 at 11:17
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.