From 397214caf0fe46cb1cd455908a67d36a931e7ec4 Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Tue, 6 Sep 2022 21:00:00 -0400 Subject: Finish bidding table component --- webapp/src/bridge_engine.rs | 32 +++++++++++++++++++++++---- webapp/src/default.css | 53 +++++++++++++++++++++++++++++++++++++++++++++ webapp/src/main.rs | 35 ++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 11 deletions(-) (limited to 'webapp/src') diff --git a/webapp/src/bridge_engine.rs b/webapp/src/bridge_engine.rs index 702faa9..573b2f1 100644 --- a/webapp/src/bridge_engine.rs +++ b/webapp/src/bridge_engine.rs @@ -143,6 +143,18 @@ impl fmt::Debug for Bid { } } +impl FromStr for Bid { + type Err = anyhow::Error; + fn from_str(s: &str) -> std::result::Result::Err> { + match s.trim().to_ascii_lowercase().as_str() { + "pass" => Ok(Bid::Pass), + "double" => Ok(Bid::Double), + "redouble" => Ok(Bid::Redouble), + x => Ok(Bid::Raise(x.parse()?)), + } + } +} + #[derive(PartialEq, Eq, Clone, Copy)] pub struct Raise { pub level: ContractLevel, @@ -216,7 +228,7 @@ impl FromStr for Raise { let caps = RE.captures(s).ok_or(anyhow!("invalid raise: {}", s))?; debug!("caps: {:?}", caps); let level = caps[1].parse()?; - let suit = match &caps[2] { + let suit = match caps[2].to_ascii_uppercase().as_str() { "NT" => None, x => Some(x.parse()?), }; @@ -224,14 +236,14 @@ impl FromStr for Raise { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ContractModifier { None, Doubled, Redoubled, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct Contract { highest_bid: Option, modifier: ContractModifier, @@ -295,12 +307,21 @@ impl Bidding { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum BiddingResult { InProgress(Bidding), Contract(Contract, Bidding), } +impl BiddingResult { + pub fn bidding(self) -> Bidding { + match self { + BiddingResult::InProgress(bidding) => bidding, + BiddingResult::Contract(_, bidding) => bidding, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -349,9 +370,12 @@ mod tests { assert_eq!("1♢", format!("{:?}", bid1d)); assert_eq!(bid1d, Raise::from_str("1D").unwrap()); + assert_eq!(Bid::Pass, Bid::from_str("pass").unwrap()); + let mut checked_raises = 0; for bid in Raise::all_raises() { assert_eq!(bid, Raise::from_str(format!("{}", bid).as_str()).unwrap()); + assert_eq!(Bid::Raise(bid), Bid::from_str(format!("{}", bid).as_str()).unwrap()); checked_raises += 1; } assert_eq!(checked_raises, 35); diff --git a/webapp/src/default.css b/webapp/src/default.css index d4f291b..e76ca28 100644 --- a/webapp/src/default.css +++ b/webapp/src/default.css @@ -62,6 +62,59 @@ body { content: "♣" } +.bidding-table { + max-width: 250px; + display: grid; + grid-template-columns: repeat(4, 1fr); + border: 2px solid #000; + border-radius: 10px; + background-color: #eee; + padding: 3px; +} +.bidding-table .header { + font-weight: bold; + border-bottom: 2px solid #000; +} + +.bidding-table .bid { + display: flex; +} + +.bidding-table .suit-spade { + color: #000; +} +.bidding-table .suit-heart { + color: #d00; +} +.bidding-table .suit-diamond { + color: #d00; +} +.bidding-table .suit-club { + color: #000; +} +.bidding-table .suit-notrump { + background-color: #dde; + border-radius: 4px; + display: flex; + align-items: center; +} + +.bidding-table .suit-spade:after { + content: "♠" +} +.bidding-table .suit-heart:after { + content: "♥" +} +.bidding-table .suit-diamond:after { + content: "♦" +} +.bidding-table .suit-club:after { + content: "♣" +} +.bidding-table .suit-notrump:after { + content: "NT" +} + .bidding-box { max-width: 250px; display: grid; diff --git a/webapp/src/main.rs b/webapp/src/main.rs index 81b8b39..846514a 100644 --- a/webapp/src/main.rs +++ b/webapp/src/main.rs @@ -4,6 +4,7 @@ use log::{debug, error, info, warn}; use yew::prelude::*; pub mod bridge_engine; pub mod card; +use bridge_engine::Bid; use bridge_engine::Bidding; use bridge_engine::Player; use bridge_engine::Raise; @@ -49,9 +50,20 @@ pub fn app() -> Html { }) }; + let bidding = Bidding::new(Player::North); + let bidding = bidding.bid("1NT".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("pass".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("2♦".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("pass".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("2♥".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("pass".parse().unwrap()).unwrap().bidding(); + let bidding = bidding.bid("4♥".parse().unwrap()).unwrap().bidding(); + html! { <> -

{ "Bids" }

+

{ "Bidding table" }

+ +

{ "Bidding box" }

().unwrap()} />

{ "North" }

@@ -145,7 +157,7 @@ fn padding(dealer: Player) -> Html { let mut padding : Vec = vec![]; let mut player = Player::West; while player != dealer { - padding.push(html! {
}); + padding.push(html! {
}); player = player.next(); } padding.into_iter().collect() @@ -153,19 +165,28 @@ fn padding(dealer: Player) -> Html { #[function_component(BiddingTable)] pub fn bidding_table(props: &BiddingTableProps) -> Html { + let bid = |bid: &Bid| match bid.as_raise() { + None => html!{
{ bid }
}, + Some(raise) => html!{ +
+ { raise.level } +
+
+ }, + }; let bids: Html = props .bidding .bids .iter() - .map(|bid| { - html! { -
{ bid }
- } - }) + .map(|b| { bid(b) }) .collect(); let padding : Html = padding(props.bidding.dealer); html! {
+
{ "West" }
+
{ "North" }
+
{ "East" }
+
{ "South" }
{ padding } { bids }
-- cgit v1.2.3