modes:triple: finish ordering and footstools for Triples
This commit is contained in:
@@ -1,20 +1,22 @@
|
|||||||
use crate::{card::Card, helper::ordered};
|
use crate::{card::Card, helper::ordered};
|
||||||
|
|
||||||
#[derive(Eq, Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Triple(Card, Card, Card);
|
pub struct Triple(Card, Card, Card);
|
||||||
|
|
||||||
impl Triple {
|
impl Triple {
|
||||||
|
/** Create a new triple utilising 3 cards: `c1`, `c2`, and `c3`. Will
|
||||||
|
return None iff a Triple cannot be constructed out of those 3 cards.
|
||||||
|
|
||||||
|
NOTE: By construction, if a triple includes 1 Joker, then Triple::0 is that
|
||||||
|
joker. If a triple includes 2 jokers, then Triple::0 and Triple::1 are
|
||||||
|
those jokers. This means Triple::2 will always be a valid playing card.
|
||||||
|
*/
|
||||||
pub fn new(c1: Card, c2: Card, c3: Card) -> Option<Triple> {
|
pub fn new(c1: Card, c2: Card, c3: Card) -> Option<Triple> {
|
||||||
let [c1, c2, c3] = ordered([c1, c2, c3]);
|
let [c1, c2, c3] = ordered([c1, c2, c3]);
|
||||||
|
|
||||||
match [c1, c2, c3].map(|c| c.rank()) {
|
match [c1, c2, c3].map(|c| c.rank()) {
|
||||||
// Two Jokers + any PlayingCard
|
|
||||||
[None, None, Some(_)] => Some(Triple(c1, c2, c3)),
|
[None, None, Some(_)] => Some(Triple(c1, c2, c3)),
|
||||||
|
|
||||||
// One Joker + two PlayingCards of the same rank
|
|
||||||
[None, Some(r2), Some(r3)] if r2 == r3 => Some(Triple(c1, c2, c3)),
|
[None, Some(r2), Some(r3)] if r2 == r3 => Some(Triple(c1, c2, c3)),
|
||||||
|
|
||||||
// Three PlayingCards of the same rank
|
|
||||||
[Some(r1), Some(r2), Some(r3)] if r1 == r2 && r2 == r3 => {
|
[Some(r1), Some(r2), Some(r3)] if r1 == r2 && r2 == r3 => {
|
||||||
Some(Triple(c1, c2, c3))
|
Some(Triple(c1, c2, c3))
|
||||||
}
|
}
|
||||||
@@ -22,47 +24,94 @@ impl Triple {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn count_jokers(&self) -> usize {
|
||||||
|
[self.0, self.1].iter().filter(|c| c.is_joker()).count()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::helper::impl_cmp_eq_on_ord;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
impl Ord for Triple {
|
impl Ord for Triple {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
/*
|
// We count jokers as part of our ordering.
|
||||||
Like pairs, we'll do a high-to-low comparison. Since we're dealing with
|
let self_jokers = self.count_jokers();
|
||||||
3 items we'll need to compute potentially 3 comparisons.
|
let other_jokers = other.count_jokers();
|
||||||
*/
|
|
||||||
let Triple(s1, s2, s3) = self;
|
let Triple(s1, s2, s3) = self;
|
||||||
let Triple(o1, o2, o3) = other;
|
let Triple(o1, o2, o3) = other;
|
||||||
|
|
||||||
let cmp = s3.cmp(o3);
|
// The most critical part of ordering is top card comparison
|
||||||
if cmp != Ordering::Equal {
|
s3.rank()
|
||||||
cmp
|
.cmp(&o3.rank())
|
||||||
} else {
|
// if we have 2 triples, both with the same ranked high card, and
|
||||||
let cmp = s2.cmp(o2);
|
// one has 2 jokers while the other doesn't => the 2 joker triple is
|
||||||
if cmp != Ordering::Equal {
|
// worse.
|
||||||
cmp
|
.then_with(|| match (self_jokers, other_jokers) {
|
||||||
} else {
|
(2, x) if x < 2 => Ordering::Less,
|
||||||
s1.cmp(o1)
|
(x, 2) if x < 2 => Ordering::Greater,
|
||||||
|
_ => Ordering::Equal,
|
||||||
|
})
|
||||||
|
// then compare by the highest to lowest cards
|
||||||
|
.then_with(|| s3.suit().cmp(&o3.suit()))
|
||||||
|
.then_with(|| s2.cmp(o2))
|
||||||
|
.then_with(|| s1.cmp(o1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_cmp_eq_on_ord!(Triple);
|
||||||
|
|
||||||
|
use crate::modes::{pair::Pair, Footstool, Hand};
|
||||||
|
|
||||||
|
impl Hand for Triple {
|
||||||
|
fn is_proper(&self) -> bool {
|
||||||
|
![self.0, self.1].iter().any(|c| c.is_joker())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn footstool(&self, other: &Self) -> Footstool {
|
||||||
|
match self.cmp(other) {
|
||||||
|
// There is no footstool if self is beaten by other.
|
||||||
|
Ordering::Less => Footstool::None,
|
||||||
|
// We can only full footstool if we have equivalent pairs.
|
||||||
|
Ordering::Equal => Footstool::Full,
|
||||||
|
// Half footstools can only proc if the 2 high cards of each hand
|
||||||
|
// footstool each other using Pair rules.
|
||||||
|
Ordering::Greater => {
|
||||||
|
// By construction, Triple::1 and Triple::2 should always make a
|
||||||
|
// Pair so it's cool to unwrap.
|
||||||
|
let p1 = Pair::new(self.1, self.2).unwrap();
|
||||||
|
let p2 = Pair::new(other.1, other.2).unwrap();
|
||||||
|
match p1.footstool(&p2) {
|
||||||
|
Footstool::None => Footstool::None,
|
||||||
|
_ => Footstool::Half,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Triple {
|
use std::fmt::{Display, Formatter, Result};
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.cmp(other) == Ordering::Equal
|
impl Display for Triple {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
|
write!(f, "Triple[{}, {}, {}]", self.0, self.1, self.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Triple {
|
use std::hash::{Hash, Hasher};
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
impl Hash for Triple {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
// Pairs are just tuples lol.
|
||||||
|
(self.0, self.1, self.2).hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::card::PlayingCard;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -84,7 +133,11 @@ mod tests {
|
|||||||
"Expected ({card}, {joker}, {joker}) to make a triple"
|
"Expected ({card}, {joker}, {joker}) to make a triple"
|
||||||
);
|
);
|
||||||
let trip = trip.unwrap();
|
let trip = trip.unwrap();
|
||||||
assert_eq!(trip.2, card, "Expected the highest card of the triple ({}) to be the sole PlayingCard ({card})", trip.2);
|
assert_eq!(
|
||||||
|
trip.2, card,
|
||||||
|
"Expected the highest card of the triple ({}) to be the sole PlayingCard ({card})",
|
||||||
|
trip.2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!("Finish implementing");
|
todo!("Finish implementing");
|
||||||
|
|||||||
Reference in New Issue
Block a user