1use crate::{Bid, Penalty};
16use core::borrow::Borrow;
17use core::fmt::{self, Write as _};
18use core::ops::Deref;
19use core::str::FromStr;
20use thiserror::Error;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27#[cfg_attr(
28 feature = "serde",
29 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
30)]
31pub enum Call {
32 Pass,
34 Double,
36 Redouble,
38 Bid(Bid),
40}
41
42impl From<Bid> for Call {
43 fn from(bid: Bid) -> Self {
44 Self::Bid(bid)
45 }
46}
47
48impl fmt::Display for Call {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Pass => f.write_char('P'),
52 Self::Double => f.write_char('X'),
53 Self::Redouble => f.write_str("XX"),
54 Self::Bid(bid) => bid.fmt(f),
55 }
56 }
57}
58
59#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
61#[error("Invalid call: expected pass, double, redouble, or a bid like '1NT' or '3♠'")]
62pub struct ParseCallError;
63
64impl FromStr for Call {
65 type Err = ParseCallError;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 match s.to_ascii_uppercase().as_str() {
69 "P" | "PASS" => Ok(Self::Pass),
70 "X" | "DBL" | "DOUBLE" => Ok(Self::Double),
71 "XX" | "RDBL" | "REDOUBLE" => Ok(Self::Redouble),
72 _ => s.parse::<Bid>().map(Self::Bid).map_err(|_| ParseCallError),
73 }
74 }
75}
76
77bitflags::bitflags! {
78 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81 pub struct RelativeVulnerability: u8 {
82 const WE = 1;
84 const THEY = 2;
86 }
87}
88
89impl RelativeVulnerability {
90 pub const NONE: Self = Self::empty();
92 pub const ALL: Self = Self::all();
94}
95
96impl fmt::Display for RelativeVulnerability {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match *self {
99 Self::NONE => f.write_str("none"),
100 Self::WE => f.write_str("we"),
101 Self::THEY => f.write_str("they"),
102 Self::ALL => f.write_str("both"),
103 _ => unreachable!("RelativeVulnerability has only 4 valid bit combinations"),
104 }
105 }
106}
107
108#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
110#[error("Invalid relative vulnerability: expected one of none, we, they, both, all")]
111pub struct ParseRelativeVulnerabilityError;
112
113impl FromStr for RelativeVulnerability {
114 type Err = ParseRelativeVulnerabilityError;
115
116 fn from_str(s: &str) -> Result<Self, Self::Err> {
117 match s.to_ascii_lowercase().as_str() {
118 "none" => Ok(Self::NONE),
119 "we" => Ok(Self::WE),
120 "they" => Ok(Self::THEY),
121 "both" | "all" => Ok(Self::ALL),
122 _ => Err(ParseRelativeVulnerabilityError),
123 }
124 }
125}
126
127#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135#[non_exhaustive]
136pub enum IllegalCall {
137 #[error("Law 27: insufficient bid")]
139 InsufficientBid {
140 this: Bid,
142 last: Option<Bid>,
144 },
145
146 #[error("Law 36: inadmissible doubles and redoubles")]
148 InadmissibleDouble(Penalty),
149
150 #[error("Law 39: call after the final pass")]
152 AfterFinalPass,
153}
154
155#[derive(Debug, Clone, Default, PartialEq, Eq)]
157#[cfg_attr(
158 feature = "serde",
159 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
160)]
161pub struct Auction(Vec<Call>);
162
163impl fmt::Display for Auction {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 let mut iter = self.0.iter();
166 if let Some(first) = iter.next() {
167 first.fmt(f)?;
168 for call in iter {
169 f.write_char(' ')?;
170 call.fmt(f)?;
171 }
172 }
173 Ok(())
174 }
175}
176
177#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
179pub enum ParseAuctionError {
180 #[error(transparent)]
182 Call(#[from] ParseCallError),
183 #[error(transparent)]
185 Illegal(#[from] IllegalCall),
186}
187
188impl FromStr for Auction {
189 type Err = ParseAuctionError;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 let mut auction = Self::new();
193 for token in s.split_ascii_whitespace() {
194 auction.try_push(token.parse()?)?;
195 }
196 Ok(auction)
197 }
198}
199
200impl Deref for Auction {
202 type Target = [Call];
203
204 fn deref(&self) -> &[Call] {
205 &self.0
206 }
207}
208
209impl AsRef<[Call]> for Auction {
210 fn as_ref(&self) -> &[Call] {
211 self
212 }
213}
214
215impl Borrow<[Call]> for Auction {
216 fn borrow(&self) -> &[Call] {
217 self
218 }
219}
220
221impl From<Auction> for Vec<Call> {
222 fn from(auction: Auction) -> Self {
223 auction.0
224 }
225}
226
227impl IntoIterator for Auction {
228 type Item = Call;
229 type IntoIter = std::vec::IntoIter<Call>;
230
231 fn into_iter(self) -> Self::IntoIter {
232 self.0.into_iter()
233 }
234}
235
236impl<'a> IntoIterator for &'a Auction {
237 type Item = &'a Call;
238 type IntoIter = core::slice::Iter<'a, Call>;
239
240 fn into_iter(self) -> Self::IntoIter {
241 self.0.iter()
242 }
243}
244
245impl Auction {
246 #[must_use]
248 pub const fn new() -> Self {
249 Self(Vec::new())
250 }
251
252 #[must_use]
255 pub fn has_ended(&self) -> bool {
256 self.len() >= 4 && self[self.len() - 3..] == [Call::Pass; 3]
257 }
258
259 fn can_double(&self) -> Result<(), IllegalCall> {
261 let admissible = self
262 .iter()
263 .rev()
264 .copied()
265 .enumerate()
266 .find(|&(_, call)| call != Call::Pass)
267 .is_some_and(|(index, call)| index & 1 == 0 && matches!(call, Call::Bid(_)));
268
269 if !admissible {
270 return Err(IllegalCall::InadmissibleDouble(Penalty::Doubled));
271 }
272 Ok(())
273 }
274
275 fn can_redouble(&self) -> Result<(), IllegalCall> {
277 let admissible = self
278 .iter()
279 .rev()
280 .copied()
281 .enumerate()
282 .find(|&(_, call)| call != Call::Pass)
283 .is_some_and(|(index, call)| index & 1 == 0 && call == Call::Double);
284
285 if !admissible {
286 return Err(IllegalCall::InadmissibleDouble(Penalty::Redoubled));
287 }
288 Ok(())
289 }
290
291 fn can_bid(&self, bid: Bid) -> Result<(), IllegalCall> {
293 let last = self.iter().rev().find_map(|&call| match call {
294 Call::Bid(bid) => Some(bid),
295 _ => None,
296 });
297
298 if last >= Some(bid) {
299 return Err(IllegalCall::InsufficientBid { this: bid, last });
300 }
301 Ok(())
302 }
303
304 fn can_push(&self, call: Call) -> Result<(), IllegalCall> {
306 if self.has_ended() {
307 return Err(IllegalCall::AfterFinalPass);
308 }
309
310 match call {
311 Call::Pass => Ok(()),
312 Call::Double => self.can_double(),
313 Call::Redouble => self.can_redouble(),
314 Call::Bid(bid) => self.can_bid(bid),
315 }
316 }
317
318 pub fn push(&mut self, call: Call) {
324 self.try_push(call).unwrap();
325 }
326
327 pub fn try_push(&mut self, call: Call) -> Result<(), IllegalCall> {
336 self.can_push(call)?;
337 self.0.push(call);
338 Ok(())
339 }
340
341 pub fn try_extend(&mut self, iter: impl IntoIterator<Item = Call>) -> Result<(), IllegalCall> {
349 let iter = iter.into_iter();
350
351 if let Some(size) = iter.size_hint().1 {
352 self.0.reserve(size);
353 }
354
355 for call in iter {
356 self.try_push(call)?;
357 }
358 Ok(())
359 }
360
361 pub fn pop(&mut self) -> Option<Call> {
363 self.0.pop()
364 }
365
366 pub fn truncate(&mut self, len: usize) {
370 self.0.truncate(len);
371 }
372
373 #[must_use]
408 pub fn declarer(&self) -> Option<usize> {
409 let (parity, strain) =
410 self.iter()
411 .copied()
412 .enumerate()
413 .rev()
414 .find_map(|(index, call)| match call {
415 Call::Bid(bid) => Some((index & 1, bid.strain)),
416 _ => None,
417 })?;
418
419 self.iter()
420 .skip(parity)
421 .step_by(2)
422 .position(|call| match call {
423 Call::Bid(bid) => bid.strain == strain,
424 _ => false,
425 })
426 .map(|position| position << 1 | parity)
427 }
428}
429
430#[cfg(test)]
431mod tests;