1use crate::deal::{Builder, FullDeal, PartialDeal};
4use crate::seat::Seat;
5use crate::{Strain, Suit};
6
7use dds_bridge_sys as sys;
8use thiserror::Error;
9
10use core::ffi::c_int;
11use core::fmt;
12
13#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
17#[error("trick count must be in 0..=13")]
18pub struct InvalidTrickCount;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(feature = "serde", serde(transparent))]
28#[repr(transparent)]
29pub struct TrickCount(u8);
30
31impl TrickCount {
32 #[must_use]
39 #[inline]
40 pub const fn new(n: u8) -> Self {
41 match Self::try_new(n) {
42 Ok(tc) => tc,
43 Err(_) => panic!("trick count must be in 0..=13"),
44 }
45 }
46
47 #[inline]
53 pub const fn try_new(n: u8) -> Result<Self, InvalidTrickCount> {
54 if n > 13 {
55 return Err(InvalidTrickCount);
56 }
57 Ok(Self(n))
58 }
59
60 #[must_use]
62 #[inline]
63 pub const fn get(self) -> u8 {
64 self.0
65 }
66}
67
68impl From<TrickCount> for u8 {
69 #[inline]
70 fn from(tc: TrickCount) -> Self {
71 tc.0
72 }
73}
74
75impl From<TrickCount> for usize {
76 #[inline]
77 fn from(tc: TrickCount) -> Self {
78 tc.0 as Self
79 }
80}
81
82impl fmt::Display for TrickCount {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 self.0.fmt(f)
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91#[cfg_attr(feature = "serde", serde(transparent))]
92#[repr(transparent)]
93pub struct TrickCountRow(u16);
94
95impl TrickCountRow {
96 #[must_use]
103 #[inline]
104 pub const fn new(n: u8, e: u8, s: u8, w: u8) -> Self {
105 match Self::try_new(n, e, s, w) {
106 Ok(row) => row,
107 Err(_) => panic!("trick count must be in 0..=13"),
108 }
109 }
110
111 #[inline]
117 pub const fn try_new(n: u8, e: u8, s: u8, w: u8) -> Result<Self, InvalidTrickCount> {
118 if n > 13 || e > 13 || s > 13 || w > 13 {
119 return Err(InvalidTrickCount);
120 }
121 Ok(Self(
122 (n as u16) << (4 * Seat::North as u8)
123 | (e as u16) << (4 * Seat::East as u8)
124 | (s as u16) << (4 * Seat::South as u8)
125 | (w as u16) << (4 * Seat::West as u8),
126 ))
127 }
128
129 #[must_use]
131 pub const fn get(self, seat: Seat) -> TrickCount {
132 TrickCount((self.0 >> (4 * seat as u8) & 0xF) as u8)
133 }
134
135 #[must_use]
137 pub const fn hex(self, seat: Seat) -> TrickCountRowHex {
138 TrickCountRowHex { row: self, seat }
139 }
140}
141
142#[derive(Debug, Clone, Copy)]
148pub struct TrickCountRowHex {
149 row: TrickCountRow,
150 seat: Seat,
151}
152
153impl fmt::UpperHex for TrickCountRowHex {
154 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155 write!(
156 f,
157 "{:X}{:X}{:X}{:X}",
158 self.row.get(self.seat).get(),
159 self.row.get(self.seat.lho()).get(),
160 self.row.get(self.seat.partner()).get(),
161 self.row.get(self.seat.rho()).get(),
162 )
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169#[cfg_attr(feature = "serde", serde(transparent))]
170#[repr(transparent)]
171pub struct TrickCountTable(pub [TrickCountRow; 5]);
172
173impl core::ops::Index<Strain> for TrickCountTable {
174 type Output = TrickCountRow;
175
176 fn index(&self, strain: Strain) -> &TrickCountRow {
177 &self.0[strain as usize]
178 }
179}
180
181impl TrickCountTable {
182 #[must_use]
184 pub const fn hex<T: AsRef<[Strain]>>(self, seat: Seat, strains: T) -> TrickCountTableHex<T> {
185 TrickCountTableHex {
186 table: self,
187 seat,
188 strains,
189 }
190 }
191}
192
193#[derive(Debug, Clone, Copy)]
199pub struct TrickCountTableHex<T: AsRef<[Strain]>> {
200 table: TrickCountTable,
201 seat: Seat,
202 strains: T,
203}
204
205impl<T: AsRef<[Strain]>> fmt::UpperHex for TrickCountTableHex<T> {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 for &strain in self.strains.as_ref() {
208 self.table[strain].hex(self.seat).fmt(f)?;
209 }
210 Ok(())
211 }
212}
213
214impl Strain {
215 #[must_use]
217 const fn to_sys(self) -> usize {
218 match self {
219 Self::Spades => 0,
220 Self::Hearts => 1,
221 Self::Diamonds => 2,
222 Self::Clubs => 3,
223 Self::Notrump => 4,
224 }
225 }
226}
227
228impl From<sys::ddTableResults> for TrickCountTable {
229 fn from(table: sys::ddTableResults) -> Self {
230 use super::ffi::trick_count_from_sys;
231 let row = |r: [c_int; 4]| {
232 TrickCountRow::new(
233 trick_count_from_sys(r[0]).get(),
234 trick_count_from_sys(r[1]).get(),
235 trick_count_from_sys(r[2]).get(),
236 trick_count_from_sys(r[3]).get(),
237 )
238 };
239
240 Self([
241 row(table.resTable[Strain::Clubs.to_sys()]),
242 row(table.resTable[Strain::Diamonds.to_sys()]),
243 row(table.resTable[Strain::Hearts.to_sys()]),
244 row(table.resTable[Strain::Spades.to_sys()]),
245 row(table.resTable[Strain::Notrump.to_sys()]),
246 ])
247 }
248}
249
250impl From<TrickCountTable> for sys::ddTableResults {
251 fn from(table: TrickCountTable) -> Self {
252 const fn make_row(row: TrickCountRow) -> [c_int; 4] {
253 [
254 row.get(Seat::North).get() as c_int,
255 row.get(Seat::East).get() as c_int,
256 row.get(Seat::South).get() as c_int,
257 row.get(Seat::West).get() as c_int,
258 ]
259 }
260
261 Self {
262 resTable: [
263 make_row(table[Strain::Spades]),
264 make_row(table[Strain::Hearts]),
265 make_row(table[Strain::Diamonds]),
266 make_row(table[Strain::Clubs]),
267 make_row(table[Strain::Notrump]),
268 ],
269 }
270 }
271}
272
273impl From<Builder> for sys::ddTableDeal {
276 fn from(builder: Builder) -> Self {
277 Self {
278 cards: Seat::ALL.map(|seat| {
279 let hand = builder[seat];
280 [
281 hand[Suit::Spades].to_bits().into(),
282 hand[Suit::Hearts].to_bits().into(),
283 hand[Suit::Diamonds].to_bits().into(),
284 hand[Suit::Clubs].to_bits().into(),
285 ]
286 }),
287 }
288 }
289}
290
291impl From<FullDeal> for sys::ddTableDeal {
292 #[inline]
293 fn from(deal: FullDeal) -> Self {
294 Builder::from(deal).into()
295 }
296}
297
298impl From<PartialDeal> for sys::ddTableDeal {
299 #[inline]
300 fn from(subset: PartialDeal) -> Self {
301 Builder::from(subset).into()
302 }
303}