1use super::ffi;
4use crate::contract::{Bid, Contract, Penalty};
5use crate::seat::Seat;
6use core::ops::BitOr as _;
7use dds_bridge_sys as sys;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct ParContract {
13 pub contract: Contract,
15
16 pub declarer: Seat,
18
19 pub overtricks: i8,
21}
22
23#[derive(Debug, Clone)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct Par {
27 pub score: i32,
29
30 pub contracts: Vec<ParContract>,
32}
33
34impl Par {
35 #[must_use]
48 pub fn equivalent(&self, other: &Self) -> bool {
49 fn key(contracts: &[ParContract]) -> u32 {
53 contracts
54 .iter()
55 .map(|p| 1 << ((p.contract.bid.strain as u8) << 2 | p.declarer as u8))
56 .fold(0, u32::bitor)
57 }
58 self.score == other.score && key(&self.contracts) == key(&other.contracts)
59 }
60}
61
62impl From<sys::parResultsMaster> for Par {
63 fn from(par: sys::parResultsMaster) -> Self {
64 let number = ffi::count_from_sys(par.number, par.contracts.len());
65
66 let len = number * usize::from(par.contracts[0].level != 0);
69
70 let contracts = par.contracts[..len]
71 .iter()
72 .flat_map(|contract| {
73 let strain = ffi::strain_from_denom(contract.denom);
74
75 #[allow(clippy::cast_possible_truncation)]
76 let (penalty, overtricks) = if contract.underTricks > 0 {
77 (Penalty::Doubled, -contract.underTricks as i8)
78 } else {
79 (Penalty::Undoubled, contract.overTricks as i8)
80 };
81
82 let seat = match contract.seats & 3 {
83 0 => Seat::North,
84 1 => Seat::East,
85 2 => Seat::South,
86 3 => Seat::West,
87 _ => unreachable!("The bitmask ensures this is always in 0..=3"),
88 };
89 let is_pair = contract.seats >= 4;
90
91 let contract = Contract {
92 bid: Bid {
93 level: ffi::level_from_sys(contract.level),
94 strain,
95 },
96 penalty,
97 };
98
99 core::iter::once(ParContract {
100 contract,
101 declarer: seat,
102 overtricks,
103 })
104 .chain(is_pair.then_some(ParContract {
105 contract,
106 declarer: seat.partner(),
107 overtricks,
108 }))
109 })
110 .collect();
111
112 Self {
113 score: par.score,
114 contracts,
115 }
116 }
117}