cardspy 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cardspy-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.3
2
+ Name: cardspy
3
+ Version: 0.0.1
4
+ Summary: Functionalities for playing cards
5
+ Author: bd.noads@gmail.com
6
+ Requires-Python: >=3.10
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Requires-Dist: mypy (>=1.15.0,<2.0.0)
13
+ Description-Content-Type: text/markdown
14
+
15
+ # cardspy
@@ -0,0 +1 @@
1
+ # cardspy
File without changes
@@ -0,0 +1,177 @@
1
+ """Card"""
2
+ from typing import List
3
+ from functools import reduce
4
+ from dataclasses import dataclass
5
+ from .rank import (
6
+ Rank,
7
+ R2,
8
+ R3,
9
+ R4,
10
+ R5,
11
+ R6,
12
+ R7,
13
+ R8,
14
+ R9,
15
+ RT,
16
+ RJ,
17
+ RQ,
18
+ RK,
19
+ RA,
20
+ )
21
+ from .suit import Suit, CLUB, DIAMOND, HEART, SPADE
22
+
23
+
24
+ @dataclass
25
+ class Card:
26
+ """Card"""
27
+ key: int
28
+ rank: Rank
29
+ suit: Suit
30
+ code: str
31
+ name: str
32
+ symbol: str
33
+
34
+ def __str__(self) -> str:
35
+ return self.symbol
36
+
37
+ def __repr__(self) -> str:
38
+ return self.__str__()
39
+
40
+ def __lt__(self, other: "Card") -> bool:
41
+ return self.key < other.key
42
+
43
+ def __le__(self, other: "Card") -> bool:
44
+ return self.key <= other.key
45
+
46
+ def __gt__(self, other: "Card") -> bool:
47
+ return self.key > other.key
48
+
49
+ def __ge__(self, other: "Card") -> bool:
50
+ return self.key >= other.key
51
+
52
+ def __eq__(self, other: object) -> bool:
53
+ if not isinstance(other, Card):
54
+ return NotImplemented
55
+ return self.key == other.key
56
+
57
+ def __ne__(self, other: object) -> bool:
58
+ if not isinstance(other, Card):
59
+ return NotImplemented
60
+ return self.key != other.key
61
+
62
+
63
+ C2C = Card(0x1, R2, CLUB, "2C", "Two of Clubs", "2♣")
64
+ C2D = Card(0x2, R2, DIAMOND, "2D", "Two of Diamonds", "2♦")
65
+ C2H = Card(0x4, R2, HEART, "2H", "Two of Hearts", "2♥")
66
+ C2S = Card(0x8, R2, SPADE, "2S", "Two of Spades", "2♠")
67
+
68
+ C3C = Card(0x10, R3, CLUB, "3C", "Three of Clubs", "3♣")
69
+ C3D = Card(0x20, R3, DIAMOND, "3D", "Three of Diamonds", "3♦")
70
+ C3H = Card(0x40, R3, HEART, "3H", "Three of Hearts", "3♥")
71
+ C3S = Card(0x80, R3, SPADE, "3S", "Three of Spades", "3♠")
72
+
73
+ C4C = Card(0x100, R4, CLUB, "4C", "Four of Clubs", "4♣")
74
+ C4D = Card(0x200, R4, DIAMOND, "4D", "Four of Diamonds", "4♦")
75
+ C4H = Card(0x400, R4, HEART, "4H", "Four of Hearts", "4♥")
76
+ C4S = Card(0x800, R4, SPADE, "4S", "Four of Spades", "4♠")
77
+
78
+ C5C = Card(0x1000, R5, CLUB, "5C", "Five of Clubs", "5♣")
79
+ C5D = Card(0x2000, R5, DIAMOND, "5D", "Five of Diamonds", "5♦")
80
+ C5H = Card(0x4000, R5, HEART, "5H", "Five of Hearts", "5♥")
81
+ C5S = Card(0x8000, R5, SPADE, "5S", "Five of Spades", "5♠")
82
+
83
+ C6C = Card(0x10000, R6, CLUB, "6C", "Six of Clubs", "6♣")
84
+ C6D = Card(0x20000, R6, DIAMOND, "6D", "Six of Diamonds", "6♦")
85
+ C6H = Card(0x40000, R6, HEART, "6H", "Six of Hearts", "6♥")
86
+ C6S = Card(0x80000, R6, SPADE, "6S", "Six of Spades", "6♠")
87
+
88
+ C7C = Card(0x100000, R7, CLUB, "7C", "Seven of Clubs", "7♣")
89
+ C7D = Card(0x200000, R7, DIAMOND, "7D", "Seven of Diamonds", "7♦")
90
+ C7H = Card(0x400000, R7, HEART, "7H", "Seven of Hearts", "7♥")
91
+ C7S = Card(0x800000, R7, SPADE, "7S", "Seven of Spades", "7♠")
92
+
93
+ C8C = Card(0x1000000, R8, CLUB, "8C", "Eight of Clubs", "8♣")
94
+ C8D = Card(0x2000000, R8, DIAMOND, "8D", "Eight of Diamonds", "8♦")
95
+ C8H = Card(0x4000000, R8, HEART, "8H", "Eight of Hearts", "8♥")
96
+ C8S = Card(0x8000000, R8, SPADE, "8S", "Eight of Spades", "8♠")
97
+
98
+ C9C = Card(0x10000000, R9, CLUB, "9C", "Nine of Clubs", "9♣")
99
+ C9D = Card(0x20000000, R9, DIAMOND, "9D", "Nine of Diamonds", "9♦")
100
+ C9H = Card(0x40000000, R9, HEART, "9H", "Nine of Hearts", "9♥")
101
+ C9S = Card(0x80000000, R9, SPADE, "9S", "Nine of Spades", "9♠")
102
+
103
+ CTC = Card(0x100000000, RT, CLUB, "TC", "Ten of Clubs", "10♣")
104
+ CTD = Card(0x200000000, RT, DIAMOND, "TD", "Ten of Diamonds", "10♦")
105
+ CTH = Card(0x400000000, RT, HEART, "TH", "Ten of Hearts", "10♥")
106
+ CTS = Card(0x800000000, RT, SPADE, "TS", "Ten of Spades", "10♠")
107
+
108
+ CJC = Card(0x1000000000, RJ, CLUB, "JC", "Jack of Clubs", "J♣")
109
+ CJD = Card(0x2000000000, RJ, DIAMOND, "JD", "Jack of Diamonds", "J♦")
110
+ CJH = Card(0x4000000000, RJ, HEART, "JH", "Jack of Hearts", "J♥")
111
+ CJS = Card(0x8000000000, RJ, SPADE, "JS", "Jack of Spades", "J♠")
112
+
113
+ CQC = Card(0x10000000000, RQ, CLUB, "QC", "Queen of Clubs", "Q♣")
114
+ CQD = Card(0x20000000000, RQ, DIAMOND, "QD", "Queen of Diamonds", "Q♦")
115
+ CQH = Card(0x40000000000, RQ, HEART, "QH", "Queen of Hearts", "Q♥")
116
+ CQS = Card(0x80000000000, RQ, SPADE, "QS", "Queen of Spades", "Q♠")
117
+
118
+ CKC = Card(0x100000000000, RK, CLUB, "KC", "King of Clubs", "K♣")
119
+ CKD = Card(0x200000000000, RK, DIAMOND, "KD", "King of Diamonds", "K♦")
120
+ CKH = Card(0x400000000000, RK, HEART, "KH", "King of Hearts", "K♥")
121
+ CKS = Card(0x800000000000, RK, SPADE, "KS", "King of Spades", "K♠")
122
+
123
+ CAC = Card(0x1000000000000, RA, CLUB, "AC", "Ace of Clubs", "A♣")
124
+ CAD = Card(0x2000000000000, RA, DIAMOND, "AD", "Ace of Diamonds", "A♦")
125
+ CAH = Card(0x4000000000000, RA, HEART, "AH", "Ace of Hearts", "A♥")
126
+ CAS = Card(0x8000000000000, RA, SPADE, "AS", "Ace of Spades", "A♠")
127
+
128
+ ALL_CARDS = [
129
+ C2C, C2D, C2H, C2S,
130
+ C3C, C3D, C3H, C3S,
131
+ C4C, C4D, C4H, C4S,
132
+ C5C, C5D, C5H, C5S,
133
+ C6C, C6D, C6H, C6S,
134
+ C7C, C7D, C7H, C7S,
135
+ C8C, C8D, C8H, C8S,
136
+ C9C, C9D, C9H, C9S,
137
+ CTC, CTD, CTH, CTS,
138
+ CJC, CJD, CJH, CJS,
139
+ CQC, CQD, CQH, CQS,
140
+ CKC, CKD, CKH, CKS,
141
+ CAC, CAD, CAH, CAS,
142
+ ]
143
+
144
+
145
+ def sort_cards(cards: List[Card]) -> List[Card]:
146
+ """Sort cards by rank"""
147
+ return sorted(cards, key=lambda card: card.rank.key)
148
+
149
+
150
+ def cards_mask(cards: List[Card]) -> int:
151
+ """
152
+ Given an iterable of Card objects with a .id attribute (an int bit‐mask),
153
+ return the combined mask of all those cards.
154
+ """
155
+ return reduce(lambda mask, card: mask | card.key, cards, 0)
156
+
157
+
158
+ # def rank_mask_from_cards(cards: int) -> int:
159
+ # """
160
+ # Given an int bit-mask of cards, return the combined mask of
161
+ # all those cards.
162
+ # """
163
+ # rmask = 0
164
+ # for card in ALL_CARDS:
165
+ # if cards & card.key != 0:
166
+ # rmask |= card.rank.key
167
+ # return rmask
168
+
169
+
170
+ def extract_cards(cards: int) -> List[Card]:
171
+ """
172
+ Given an int bit-mask of cards, return the list of Card objects
173
+ that make up that mask.
174
+ """
175
+ return [
176
+ card for card in ALL_CARDS if cards & card.key != 0
177
+ ]
@@ -0,0 +1,118 @@
1
+ """Deck"""
2
+ from random import shuffle
3
+ from typing import List
4
+ from functools import reduce
5
+ from .rank import (
6
+ RA,
7
+ RK,
8
+ RQ,
9
+ RJ,
10
+ RT,
11
+ R9,
12
+ R8,
13
+ R7,
14
+ R6,
15
+ R5,
16
+ R4,
17
+ R3,
18
+ R2,
19
+ )
20
+ from .card import ALL_CARDS, Card, cards_mask
21
+
22
+
23
+ CLUBS = 0x1111111111111
24
+ DIAMONDS = 0x2222222222222
25
+ HEARTS = 0x4444444444444
26
+ SPADES = 0x8888888888888
27
+
28
+ SAME_RANKS = [
29
+ 0xF, 0xF0, 0xF00, 0xF000, 0xF0000, 0xF00000,
30
+ 0xF000000, 0xF0000000, 0xF00000000, 0xF000000000,
31
+ 0xF0000000000, 0xF00000000000, 0xF000000000000
32
+ ]
33
+
34
+ RANKS_DESC = [RA, RK, RQ, RJ, RT, R9, R8, R7, R6, R5, R4, R3, R2]
35
+
36
+ FULL_DECK_MASK = reduce(lambda acc, card: acc | card.key, ALL_CARDS, 0)
37
+
38
+
39
+ class Deck:
40
+ """Deck"""
41
+ def __init__(self) -> None:
42
+ self.bitmask = 0
43
+ for card in ALL_CARDS:
44
+ self.bitmask |= card.key
45
+
46
+ def clear(self) -> None:
47
+ """Clear the deck"""
48
+ self.bitmask = 0
49
+
50
+ def reset(self) -> None:
51
+ """Reset the deck"""
52
+ self.bitmask = FULL_DECK_MASK
53
+
54
+ def contains(self, cards: int) -> bool:
55
+ """Check if the deck contains a list of cards
56
+
57
+ Args:
58
+ cards (int): List of cards to check
59
+
60
+ Returns:
61
+ bool: True if the deck contains all the cards, False otherwise
62
+ """
63
+ return cards & self.bitmask == cards
64
+
65
+ def get_cards(self) -> List[Card]:
66
+ """Get all cards in the deck
67
+
68
+ Returns:
69
+ List[Card]: List of cards in the deck
70
+ """
71
+ cards: List[Card] = []
72
+ for card in ALL_CARDS:
73
+ if self.contains(card.key):
74
+ cards.append(card)
75
+ return cards
76
+
77
+ def shuffle(self) -> List[Card]:
78
+ """Shuffle the deck"""
79
+ cards = self.get_cards()
80
+ shuffle(cards)
81
+ return cards
82
+
83
+ def add_card(self, card: Card) -> None:
84
+ """Add a card to the deck"""
85
+ self.bitmask |= card.key
86
+
87
+ def remove_card(self, card: Card) -> None:
88
+ """Remove a card from the deck"""
89
+ self.bitmask &= ~card.key
90
+
91
+ def deal_cards(self, count: int = 1) -> int:
92
+ """Deal cards from the deck
93
+
94
+ Args:
95
+ count (int, optional): Number of cards to deal. Defaults to 1.
96
+
97
+ Returns:
98
+ List[Card]: List of cards dealt
99
+ """
100
+ cards: List[Card] = []
101
+ for _ in range(count):
102
+ card = self.shuffle().pop()
103
+ self.remove_card(card)
104
+ cards.append(card)
105
+ return cards_mask(cards)
106
+
107
+ def deal_specific_card(self, card: Card) -> int:
108
+ """Deal a specific card from the deck"""
109
+ if not self.contains(card.key):
110
+ raise ValueError("Card not in deck")
111
+ self.remove_card(card)
112
+ return cards_mask([card])
113
+
114
+ def deal_specific_cards(self, cards: List[Card]) -> int:
115
+ """Deal specific cards from the deck"""
116
+ for card in cards:
117
+ self.deal_specific_card(card)
118
+ return cards_mask(cards)
@@ -0,0 +1,33 @@
1
+ """Rank"""
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class Rank:
7
+ """Rank"""
8
+ key: int
9
+ code: str
10
+ name: str
11
+
12
+ def __str__(self) -> str:
13
+ return self.code
14
+
15
+ def __repr__(self) -> str:
16
+ return self.code
17
+
18
+
19
+ R2 = Rank(0x1, "2", "Two")
20
+ R3 = Rank(0x2, "3", "Three")
21
+ R4 = Rank(0x4, "4", "Four")
22
+ R5 = Rank(0x8, "5", "Five")
23
+ R6 = Rank(0x10, "6", "Six")
24
+ R7 = Rank(0x20, "7", "Seven")
25
+ R8 = Rank(0x40, "8", "Eight")
26
+ R9 = Rank(0x80, "9", "Nine")
27
+ RT = Rank(0x100, "10", "Ten")
28
+ RJ = Rank(0x200, "J", "Jack")
29
+ RQ = Rank(0x400, "Q", "Queen")
30
+ RK = Rank(0x800, "K", "King")
31
+ RA = Rank(0x1000, "A", "Ace")
32
+
33
+ NUM_RANKS = 13
@@ -0,0 +1,23 @@
1
+ """Suit"""
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class Suit:
7
+ """Suit"""
8
+ key: int
9
+ code: str
10
+ name: str
11
+ symbol: str
12
+
13
+ def __str__(self) -> str:
14
+ return self.symbol
15
+
16
+ def __repr__(self) -> str:
17
+ return self.symbol
18
+
19
+
20
+ CLUB = Suit(0x1, "C", "Club", "♣")
21
+ DIAMOND = Suit(0x2, "D", "Diamond", "♦")
22
+ HEART = Suit(0x4, "H", "Heart", "♥")
23
+ SPADE = Suit(0x8, "S", "Spade", "♠")
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "cardspy"
3
+ version = "0.0.1"
4
+ description = "Functionalities for playing cards"
5
+ authors = [
6
+ {name = "bd.noads@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "mypy (>=1.15.0,<2.0.0)"
12
+ ]
13
+
14
+ [tool.poetry]
15
+
16
+ [tool.poetry.group.dev.dependencies]
17
+ pytest = "^8.3.5"
18
+
19
+ [build-system]
20
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
21
+ build-backend = "poetry.core.masonry.api"