First commit
commit
dad07bc637
|
@ -0,0 +1,6 @@
|
||||||
|
venv/
|
||||||
|
data/
|
||||||
|
test*
|
||||||
|
math_ext.py
|
||||||
|
.idea
|
||||||
|
__pycache__/
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -0,0 +1,2 @@
|
||||||
|
pygame
|
||||||
|
signalslot
|
|
@ -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
|
Loading…
Reference in New Issue