First commit

master
En Yi 2019-05-04 11:45:21 +01:00
commit dad07bc637
6 changed files with 486 additions and 0 deletions

6
.gitignore vendored 100644
View File

@ -0,0 +1,6 @@
venv/
data/
test*
math_ext.py
.idea
__pycache__/

240
cards.py 100644
View File

@ -0,0 +1,240 @@
"""
This module contains the Card class and the Deck class
Card contains the information of a playing card
Deck is used as a Card container
"""
import pygame
import view
import os
from enum import Enum
class DeckReveal(Enum):
SHOW_ALL = 1
HIDE_ALL = 2
ANY = 3
class DeckSort(Enum):
ASCENDING = 1
DESCENDING = 2
NOSORT = 3
class Card(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, value, hidden=False, image_data=None, parent=None):
super().__init__()
self.x = x
self.y = y
self.width = width
self.height = height
self.value = value
self.hidden = hidden
self.parent = parent
if image_data:
self.image = image_data.convert()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
# Display Value for Debug Purposes
myfont = pygame.font.SysFont("None", 16)
mytext = myfont.render(str(self.value), True, (0, 0, 0))
mytext = mytext.convert_alpha()
self.image.blit(mytext, (0,0))
self._layer = 0
def get_pos(self):
return self.x, self.y
class Deck():
def __init__(self, x, y, length, width, spacing, deck_reveal=DeckReveal.SHOW_ALL,
sort_order=DeckSort.ASCENDING, vert_orientation=False, draw_from_last=False):
super().__init__()
self.x = x
self.y = y
self.length = length
self.width = width
self.default_spacing = spacing
self.deck_reveal = deck_reveal
self.vert_orientation = vert_orientation
self.draw_from_last = draw_from_last
self.sort_order = sort_order
self.cards = []
if self.is_horizontal():
self.background = pygame.Surface((self.length, self.width))
self.background.fill((0, 255, 0))
self.background = self.background.convert()
self.deck_surface = self.background.copy()
else:
self.background = pygame.Surface((self.width, self.length))
self.background.fill((0, 255, 0))
self.background = self.background.convert()
self.deck_surface = self.background.copy()
self._layer = 1
def add_card(self, card, position=0):
card.parent = self
number_of_cards = len(self.cards)
if number_of_cards == 0:
self.cards.append(card)
else:
if self.sort_order == DeckSort.NOSORT:
self.cards.insert(position, card)
else:
if self.sort_order == DeckSort.DESCENDING:
self.cards.reverse()
if card.value < self.cards[0].value:
self.cards.insert(0, card)
elif card.value > self.cards[-1].value:
self.cards.append(card)
else:
lo = 0
hi = number_of_cards
while abs(lo-hi) != 1:
pos = (lo + hi) // 2
if card.value > self.cards[pos].value:
lo = pos
else:
hi = pos
self.cards.insert(hi, card)
if self.sort_order == DeckSort.DESCENDING:
self.cards.reverse()
self.set_card_positions()
def set_card_positions(self):
number_of_cards = len(self.cards)
if self.is_horizontal():
total_card_length = self.cards[0].width + self.default_spacing * (number_of_cards-1)
if total_card_length <= self.length:
start_point = (self.length - total_card_length)/2
for (i, card) in enumerate(self.cards):
card.x = start_point + self.default_spacing * (i-1)
card.y = (self.width - self.cards[0].height)/ 2
else:
adjusted_spacing = (self.length - self.cards[0].width)/(number_of_cards-1)
start_point = 0
for (i, card) in enumerate(self.cards):
card.x = start_point + adjusted_spacing * i
card.y = (self.width - self.cards[0].height)/ 2
else:
total_card_length = self.cards[0].height + self.default_spacing * (number_of_cards-1)
if total_card_length <= self.length:
start_point = (self.length - total_card_length)/2
for (i, card) in enumerate(self.cards):
card.y = start_point + self.default_spacing * (i-1)
card.x = (self.width - self.cards[0].width)/ 2
else:
adjusted_spacing = (self.length - self.cards[0].height)/(number_of_cards-1)
start_point = 0
for (i, card) in enumerate(self.cards):
card.y = start_point + adjusted_spacing * i
card.x = (self.width - self.cards[0].width)/ 2
self.update_deck_display()
def update_deck_display(self):
self.deck_surface.blit(self.background, (0, 0))
if self.draw_from_last:
for card in reversed(self.cards):
self.deck_surface.blit(card.image, (card.x, card.y))
else:
for card in self.cards:
self.deck_surface.blit(card.image, (card.x, card.y))
def remove_card(self):
pass
def is_horizontal(self):
return not self.vert_orientation
def get_pos(self):
return self.x, self.y
def print_deck_values(self):
values = ""
for card in self.cards:
values = values + str(card.value) + ' '
print(values)
class PlayerDeck(Deck):
def get_selected_card(self, pos):
# TODO: convert pos to card num, deselect if no card is clicked or out of range
# TODO: check if card num is selected, set selected, otherwise use it (by removing)
pass
DATA_FOLDER = "data"
class test_screen(view.PygView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
try: # try to load images from the harddisk
card_img = pygame.image.load(os.path.join(DATA_FOLDER, 'diamond.jpg'))
except:
raise Exception("Cannot load image") # print error message and exit program
self.test_card = Card(50, 0, 50, 75, 111, image_data=card_img)
self.test_deck = Deck(100, 100, 200, 100, 25)
self.test_deck.add_card(Card(50, 0, 50, 75, 412, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 315, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 210, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 103, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 405, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 112, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 301, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 206, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 206, image_data=card_img))
self.test_deck.add_card(Card(50, 0, 50, 75, 206, image_data=card_img))
def draw_function(self):
self.screen.blit(self.test_card.image, self.test_card.get_pos())
self.screen.blit(self.test_deck.deck_surface, self.test_deck.get_pos())
def run(self):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_p:
print('add cards')
pass
milliseconds = self.clock.tick(self.fps)
#self.playtime += milliseconds / 1000.0
self.draw_function()
pygame.display.flip()
self.screen.blit(self.background, (0, 0))
pygame.quit()
if __name__ == '__main__':
test_view = test_screen(640, 400, clear_colour=(0, 0, 0))
test_view.run()

44
main.py 100644
View File

@ -0,0 +1,44 @@
import view
import pygame
class game_screen(view.PygView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def load_assets(self):
pass
def draw_function(self):
pass
def run(self):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_p:
print('add cards')
pass
milliseconds = self.clock.tick(self.fps)
#self.playtime += milliseconds / 1000.0
self.draw_function()
pygame.display.flip()
self.screen.blit(self.background, (0, 0))
pygame.quit()
if __name__ == '__main__':
main_view = game_screen(640, 400, clear_colour=(255, 0, 0))
main_view.run()

168
players.py 100644
View File

@ -0,0 +1,168 @@
import pygame
import cards
import view
from signalslot import Signal
class Table:
"""
A Table is the place where all actions takes place. It is essentially a FSM, doing different
routines at each state. It needs to keep track of the score, roles, and the rules. It needs
to ask each player for decisions and respond to them accordingly. The table will also need
to inform any decision to the Main Screen so that it can update the screen to reflect that
change through the use of callbacks (Signal and Slot).
FSM cycles
---
Preloop - Prepare the cards once
- Initiate Players and connect them to the Table
1. Shuffle and Deal out cards to Players.
2a. Detect weak hands and ask for reshuffle.
2b. Return to (1) if any reshuffle occurs, otherwise proceed.
3. Bidding round. Randomly pick a starting player, in clockwise manner
ask for a bid until it is valid.
3b. Proceed only if 3 consecutive skips are detected.
3c. Ask the winner of the bid a card not in their hand.
3d. Set up the player roles, trump suit, rounds to win for both side
3e. Play the game. Start with bid winner if NO TRUMP, otherwise
Starting next to the bid winner.
4a. With the first player, ask for any card, excluding trump suits if trump
is not broken
4b. With subsequent players, ask for cards that follow the suit of the first player
, include trump suit if trump is broken. Ask for any card if the player cannot
follow suit.
4c. Once all 4 players has made valid plays, announce results, update scoring. Announce
player roles if the partner card is played. Break trump if trump is played.
4d. Repeat 4 until 13 rounds are made. Maybe add early win if confirmed one side wins
5. Ask for a new game. Go back to 1 if true.
All played cards go into a hidden discard pile.
"""
update_table = Signal()
def __init__(self, x, y, width, height, clear_colour):
self.x = x
self.y = y
self.width = width
self.height = height
self.players = []
self.table_status = {'played cards': [0,0,0,0], 'leading player': 0, 'trump suit': 1,
'trump broken': False, 'round history': [], 'bid': 0}
self.background = pygame.Surface((self.width, self.height))
self.background.fill(clear_colour)
self.background = self.background.convert()
self.discard_deck = [] # This is not a deck as it will never be drawn
w_deck = (0.2*self.height, 0.2*self.width)
l_deck = (0.5*self.width, 0.5*self.height)
playerx = (self.width//2 - l_deck[0],
0,
self.width//2 - l_deck[0],
self.width - w_deck[0])
playery = (self.height - w_deck[0],
self.height//2 - l_deck[1],
0,
self.height//2 - l_deck[1])
for i in range(4):
if i == 1:
self.players.append(MainPlayer())
else:
self.players.append(Player())
class Player(cards.Deck):
"""
A player is essentiallg a Deck with decision making function or AI component if it is a bot
that returns a valid action for the Table/Board.
The player has the knowledge of Table status in the form of a dictatary (as it is mutable, thus passed by ref)
so all validation is done by the player
Possible decisions, each decision has to be enum maybe:
- Query the board status (i.e. current round, player status), AI most likely need a lot more
- Query the last round
- Attempt to play a card
- Play the validate move
"""
def __init__(self, ai_component=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.AI = ai_component
self.table_status = None # This is found in Table and updated through Table
def connect_to_table(self, table):
self.table_status = table
def make_a_bid(self):
pass
def make_a_play(self):
pass
def view_last_round(self):
pass
class MainPlayer(cards.PlayerDeck):
def __init__(self, ai_component=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.AI = ai_component
self.table_status = None # This is found in Table and updated through Table
def connect_to_table(self, table):
self.table_status = table
def make_a_bid(self):
pass
def make_a_play(self):
pass
def view_last_round(self):
pass
class TestView(view.PygView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def draw_function(self):
pass
def run(self):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_p:
print('add cards')
pass
milliseconds = self.clock.tick(self.fps)
#self.playtime += milliseconds / 1000.0
self.draw_function()
pygame.display.flip()
self.screen.blit(self.background, (0, 0))
pygame.quit()
if __name__ == '__main__':
test_view = TestView(640, 400, clear_colour=(0, 0, 0))
test_view.run()

2
requirements.txt 100644
View File

@ -0,0 +1,2 @@
pygame
signalslot

26
view.py 100644
View File

@ -0,0 +1,26 @@
import pygame
class PygView(object):
def __init__(self, width=640, height=400, fps=60, clear_colour=(0, 0, 0)):
"""Initialize pygame, window, background, font,...
"""
pygame.init()
pygame.display.set_caption("Press ESC to quit")
self.width = width
self.height = height
#self.height = width // 4
self.screen = pygame.display.set_mode((self.width, self.height), pygame.DOUBLEBUF)
self.background = pygame.Surface(self.screen.get_size())
self.background.fill(clear_colour)
self.background = self.background.convert()
self.clock = pygame.time.Clock()
self.fps = fps
#self.playtime = 0.0
self.font = pygame.font.SysFont('mono', 20, bold=True)
def run(self):
"""The mainloop
"""
pass