1use crate::{Bid, Penalty};
17use core::borrow::Borrow;
18use core::fmt::{self, Write as _};
19use core::ops::Deref;
20use core::str::FromStr;
21use thiserror::Error;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28#[cfg_attr(
29 feature = "serde",
30 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
31)]
32pub enum Call {
33 Pass,
35 Double,
37 Redouble,
39 Bid(Bid),
41}
42
43impl From<Bid> for Call {
44 fn from(bid: Bid) -> Self {
45 Self::Bid(bid)
46 }
47}
48
49impl fmt::Display for Call {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::Pass => f.write_char('P'),
53 Self::Double => f.write_char('X'),
54 Self::Redouble => f.write_str("XX"),
55 Self::Bid(bid) => bid.fmt(f),
56 }
57 }
58}
59
60#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
62#[error("Invalid call: expected pass, double, redouble, or a bid like '1NT' or '3♠'")]
63pub struct ParseCallError;
64
65impl FromStr for Call {
66 type Err = ParseCallError;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 match s.to_ascii_uppercase().as_str() {
70 "P" | "PASS" => Ok(Self::Pass),
71 "X" | "DBL" | "DOUBLE" => Ok(Self::Double),
72 "XX" | "RDBL" | "REDOUBLE" => Ok(Self::Redouble),
73 _ => s.parse::<Bid>().map(Self::Bid).map_err(|_| ParseCallError),
74 }
75 }
76}
77
78bitflags::bitflags! {
79 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82 pub struct RelativeVulnerability: u8 {
83 const WE = 1;
85 const THEY = 2;
87 }
88}
89
90impl RelativeVulnerability {
91 pub const NONE: Self = Self::empty();
93 pub const ALL: Self = Self::all();
95}
96
97impl fmt::Display for RelativeVulnerability {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match *self {
100 Self::NONE => f.write_str("none"),
101 Self::WE => f.write_str("we"),
102 Self::THEY => f.write_str("they"),
103 Self::ALL => f.write_str("both"),
104 _ => unreachable!("RelativeVulnerability has only 4 valid bit combinations"),
105 }
106 }
107}
108
109#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
111#[error("Invalid relative vulnerability: expected one of none, we, they, both, all")]
112pub struct ParseRelativeVulnerabilityError;
113
114impl FromStr for RelativeVulnerability {
115 type Err = ParseRelativeVulnerabilityError;
116
117 fn from_str(s: &str) -> Result<Self, Self::Err> {
118 match s.to_ascii_lowercase().as_str() {
119 "none" => Ok(Self::NONE),
120 "we" => Ok(Self::WE),
121 "they" => Ok(Self::THEY),
122 "both" | "all" => Ok(Self::ALL),
123 _ => Err(ParseRelativeVulnerabilityError),
124 }
125 }
126}
127
128bitflags::bitflags! {
129 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138 pub struct AbsoluteVulnerability: u8 {
139 const NS = 1;
141 const EW = 2;
143 }
144}
145
146impl AbsoluteVulnerability {
147 pub const NONE: Self = Self::empty();
149 pub const ALL: Self = Self::all();
151}
152
153impl fmt::Display for AbsoluteVulnerability {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match *self {
156 Self::NONE => f.write_str("none"),
157 Self::NS => f.write_str("ns"),
158 Self::EW => f.write_str("ew"),
159 Self::ALL => f.write_str("both"),
160 _ => unreachable!("AbsoluteVulnerability has only 4 valid bit combinations"),
161 }
162 }
163}
164
165#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
167#[error("Invalid vulnerability: expected one of none, ns, ew, both, all")]
168pub struct ParseAbsoluteVulnerabilityError;
169
170impl FromStr for AbsoluteVulnerability {
171 type Err = ParseAbsoluteVulnerabilityError;
172
173 fn from_str(s: &str) -> Result<Self, Self::Err> {
174 match s.to_ascii_lowercase().as_str() {
175 "none" => Ok(Self::NONE),
176 "ns" => Ok(Self::NS),
177 "ew" => Ok(Self::EW),
178 "both" | "all" => Ok(Self::ALL),
179 _ => Err(ParseAbsoluteVulnerabilityError),
180 }
181 }
182}
183
184#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192#[non_exhaustive]
193pub enum IllegalCall {
194 #[error("Law 27: insufficient bid")]
196 InsufficientBid {
197 this: Bid,
199 last: Option<Bid>,
201 },
202
203 #[error("Law 36: inadmissible doubles and redoubles")]
205 InadmissibleDouble(Penalty),
206
207 #[error("Law 39: call after the final pass")]
209 AfterFinalPass,
210}
211
212#[derive(Debug, Clone, Default, PartialEq, Eq)]
214#[cfg_attr(
215 feature = "serde",
216 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
217)]
218pub struct Auction(Vec<Call>);
219
220impl fmt::Display for Auction {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 let mut iter = self.0.iter();
223 if let Some(first) = iter.next() {
224 first.fmt(f)?;
225 for call in iter {
226 f.write_char(' ')?;
227 call.fmt(f)?;
228 }
229 }
230 Ok(())
231 }
232}
233
234#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
236pub enum ParseAuctionError {
237 #[error(transparent)]
239 Call(#[from] ParseCallError),
240 #[error(transparent)]
242 Illegal(#[from] IllegalCall),
243}
244
245impl FromStr for Auction {
246 type Err = ParseAuctionError;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 let mut auction = Self::new();
250 for token in s.split_ascii_whitespace() {
251 auction.try_push(token.parse()?)?;
252 }
253 Ok(auction)
254 }
255}
256
257impl Deref for Auction {
259 type Target = [Call];
260
261 fn deref(&self) -> &[Call] {
262 &self.0
263 }
264}
265
266impl AsRef<[Call]> for Auction {
267 fn as_ref(&self) -> &[Call] {
268 self
269 }
270}
271
272impl Borrow<[Call]> for Auction {
273 fn borrow(&self) -> &[Call] {
274 self
275 }
276}
277
278impl From<Auction> for Vec<Call> {
279 fn from(auction: Auction) -> Self {
280 auction.0
281 }
282}
283
284impl IntoIterator for Auction {
285 type Item = Call;
286 type IntoIter = std::vec::IntoIter<Call>;
287
288 fn into_iter(self) -> Self::IntoIter {
289 self.0.into_iter()
290 }
291}
292
293impl<'a> IntoIterator for &'a Auction {
294 type Item = &'a Call;
295 type IntoIter = core::slice::Iter<'a, Call>;
296
297 fn into_iter(self) -> Self::IntoIter {
298 self.0.iter()
299 }
300}
301
302impl Auction {
303 #[must_use]
305 pub const fn new() -> Self {
306 Self(Vec::new())
307 }
308
309 #[must_use]
312 pub fn has_ended(&self) -> bool {
313 self.len() >= 4 && self[self.len() - 3..] == [Call::Pass; 3]
314 }
315
316 fn can_double(&self) -> Result<(), IllegalCall> {
318 let admissible = self
319 .iter()
320 .rev()
321 .copied()
322 .enumerate()
323 .find(|&(_, call)| call != Call::Pass)
324 .is_some_and(|(index, call)| index & 1 == 0 && matches!(call, Call::Bid(_)));
325
326 if !admissible {
327 return Err(IllegalCall::InadmissibleDouble(Penalty::Doubled));
328 }
329 Ok(())
330 }
331
332 fn can_redouble(&self) -> Result<(), IllegalCall> {
334 let admissible = self
335 .iter()
336 .rev()
337 .copied()
338 .enumerate()
339 .find(|&(_, call)| call != Call::Pass)
340 .is_some_and(|(index, call)| index & 1 == 0 && call == Call::Double);
341
342 if !admissible {
343 return Err(IllegalCall::InadmissibleDouble(Penalty::Redoubled));
344 }
345 Ok(())
346 }
347
348 fn can_bid(&self, bid: Bid) -> Result<(), IllegalCall> {
350 let last = self.iter().rev().find_map(|&call| match call {
351 Call::Bid(bid) => Some(bid),
352 _ => None,
353 });
354
355 if last >= Some(bid) {
356 return Err(IllegalCall::InsufficientBid { this: bid, last });
357 }
358 Ok(())
359 }
360
361 fn can_push(&self, call: Call) -> Result<(), IllegalCall> {
363 if self.has_ended() {
364 return Err(IllegalCall::AfterFinalPass);
365 }
366
367 match call {
368 Call::Pass => Ok(()),
369 Call::Double => self.can_double(),
370 Call::Redouble => self.can_redouble(),
371 Call::Bid(bid) => self.can_bid(bid),
372 }
373 }
374
375 pub fn push(&mut self, call: Call) {
381 self.try_push(call).unwrap();
382 }
383
384 pub fn try_push(&mut self, call: Call) -> Result<(), IllegalCall> {
393 self.can_push(call)?;
394 self.0.push(call);
395 Ok(())
396 }
397
398 pub fn try_extend(&mut self, iter: impl IntoIterator<Item = Call>) -> Result<(), IllegalCall> {
406 let iter = iter.into_iter();
407
408 if let Some(size) = iter.size_hint().1 {
409 self.0.reserve(size);
410 }
411
412 for call in iter {
413 self.try_push(call)?;
414 }
415 Ok(())
416 }
417
418 pub fn pop(&mut self) -> Option<Call> {
420 self.0.pop()
421 }
422
423 pub fn truncate(&mut self, len: usize) {
427 self.0.truncate(len);
428 }
429
430 #[must_use]
465 pub fn declarer(&self) -> Option<usize> {
466 let (parity, strain) =
467 self.iter()
468 .copied()
469 .enumerate()
470 .rev()
471 .find_map(|(index, call)| match call {
472 Call::Bid(bid) => Some((index & 1, bid.strain)),
473 _ => None,
474 })?;
475
476 self.iter()
477 .skip(parity)
478 .step_by(2)
479 .position(|call| match call {
480 Call::Bid(bid) => bid.strain == strain,
481 _ => false,
482 })
483 .map(|position| position << 1 | parity)
484 }
485}
486
487#[cfg(test)]
488mod tests;