1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
use std::fmt;
use std::hash::Hash;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
impl Suit {
#[inline]
pub const fn all() -> [Suit; 4] {
[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades]
}
#[inline]
pub const fn symbol(&self) -> &str {
match self {
Suit::Clubs => "♣",
Suit::Diamonds => "♦",
Suit::Hearts => "♥",
Suit::Spades => "♠",
}
}
#[inline]
pub const fn is_red(&self) -> bool {
matches!(self, Suit::Diamonds | Suit::Hearts)
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Rank {
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace,
}
impl Rank {
#[inline]
pub const fn all() -> [Rank; 9] {
[
Rank::Six,
Rank::Seven,
Rank::Eight,
Rank::Nine,
Rank::Ten,
Rank::Jack,
Rank::Queen,
Rank::King,
Rank::Ace,
]
}
#[inline]
pub const fn symbol(&self) -> &str {
match self {
Rank::Six => "6",
Rank::Seven => "7",
Rank::Eight => "8",
Rank::Nine => "9",
Rank::Ten => "10",
Rank::Jack => "J",
Rank::Queen => "Q",
Rank::King => "K",
Rank::Ace => "A",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Card {
pub suit: Suit,
pub rank: Rank,
}
impl Card {
#[inline]
pub const fn new(suit: Suit, rank: Rank) -> Card {
Card { suit, rank }
}
/// Determines if this card can beat another card in Durak rules
///
/// A card can beat another card if:
/// 1. It is the same suit but higher rank, OR
/// 2. It is a trump card and the other card is not
///
/// # Arguments
/// * `other` - The attacking card to beat
/// * `trump_suit` - The current trump suit for the game
///
/// # Returns
/// `true` if this card can beat the other card, `false` otherwise
#[inline]
pub fn can_beat(&self, other: &Card, trump_suit: Suit) -> bool {
if self.suit == other.suit {
return self.rank > other.rank;
}
self.suit == trump_suit && other.suit != trump_suit
}
/// Determines if this card can be used to pass an attack in Podkidnoy Durak
///
/// A card can pass an attack if it has the same rank as the attacking card
/// (regardless of the suit)
///
/// # Arguments
/// * `other` - The attacking card to check against
///
/// # Returns
/// `true` if this card can pass the attack, `false` otherwise
#[inline]
pub fn can_pass(&self, other: &Card) -> bool {
self.rank == other.rank
}
}
impl fmt::Display for Card {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.rank.symbol(), self.suit.symbol())
}
}
#[cfg(test)]
mod tests {
// We need to import the items from the parent module (the file we're in)
// so we can use them in our tests
use super::*;
#[test]
/// Basic Rank and Suit tests
fn test_can_beat_same_suit() {
// Create two cards of the same suit.
let trump_suit = Suit::Spades;
let card1 = Card::new(Suit::Hearts, Rank::Seven); // 7 of Hearts
let card2 = Card::new(Suit::Hearts, Rank::Ten); // 10 of Hearts
// We expect card2 to beat card1 because it has a higher rank.
let card3 = Card::new(Suit::Diamonds, Rank::Seven); // 7 of Diamonds
assert!(card2.can_beat(&card1, trump_suit));
assert!(!card1.can_beat(&card2, trump_suit));
// Random other suit with lower rank
assert!(!card3.can_beat(&card3, trump_suit));
}
#[test]
/// Test that a trump card can beat a lower trump card
fn test_can_beat_trump_to_trump() {
let trump_suit = Suit::Spades;
let card1 = Card::new(Suit::Spades, Rank::Six); // 6 of Spades
let card2 = Card::new(Suit::Spades, Rank::Seven); // 7 of Spades
// We expect card2 to beat card1 because it has a higher rank.
assert!(card2.can_beat(&card1, trump_suit));
assert!(!card1.can_beat(&card2, trump_suit));
}
#[test]
/// Test that a non-trump card cannot beat a trump card
fn test_can_beat_trump() {
// Create a trump card and a non-trump card.
let trump_suit = Suit::Spades;
let trump_card = Card::new(Suit::Spades, Rank::Six); // 6 of Spades
let other_card = Card::new(Suit::Hearts, Rank::Ace); // Ace of Hearts
assert!(trump_card.can_beat(&other_card, trump_suit));
// The non-trump card cannot beat the trump card.
assert!(!other_card.can_beat(&trump_card, trump_suit));
}
}