diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2022-09-06 21:00:00 -0400 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2022-09-06 21:00:00 -0400 |
commit | 397214caf0fe46cb1cd455908a67d36a931e7ec4 (patch) | |
tree | 943cab72b679c069f93744e0bd91eba059318652 /webapp/src | |
parent | 6f2dbd6db9836b5b60c78ec457b07d1202527690 (diff) |
Finish bidding table component
Diffstat (limited to 'webapp/src')
-rw-r--r-- | webapp/src/bridge_engine.rs | 32 | ||||
-rw-r--r-- | webapp/src/default.css | 53 | ||||
-rw-r--r-- | webapp/src/main.rs | 35 |
3 files changed, 109 insertions, 11 deletions
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<Self, <Self as std::str::FromStr>::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<Raise>, 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! { <> - <p>{ "Bids" }</p> + <p>{ "Bidding table" }</p> + <BiddingTable { bidding } /> + <p>{ "Bidding box" }</p> <BiddingBox lower_limit={"1H".parse::<Raise>().unwrap()} /> <p>{ "North" }</p> <Hand ..(*north).clone() /> @@ -145,7 +157,7 @@ fn padding(dealer: Player) -> Html { let mut padding : Vec<Html> = vec![]; let mut player = Player::West; while player != dealer { - padding.push(html! { <div /> }); + padding.push(html! { <div/> }); 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!{ <div class="bid">{ bid }</div> }, + Some(raise) => html!{ + <div class="bid"> + { raise.level } + <div class={ bid_css_class(raise.suit) }/> + </div> + }, + }; let bids: Html = props .bidding .bids .iter() - .map(|bid| { - html! { - <div class="bid">{ bid }</div> - } - }) + .map(|b| { bid(b) }) .collect(); let padding : Html = padding(props.bidding.dealer); html! { <div class="bidding-table"> + <div class="header">{ "West" }</div> + <div class="header">{ "North" }</div> + <div class="header">{ "East" }</div> + <div class="header">{ "South" }</div> { padding } { bids } </div> |