diff --git a/README.md b/README.md index ff6f85ad..22263123 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Here is a rough summary of some of the data types in the code: select which `Topic`s you want to practice. - A `Description` is a string-like data type that can render certain things (e.g., bids) in a fancy way. Most string-like things in here are actually - `Description`s (`Topic` names, bidding alerts, explanations of why the answer + `Description`s (bidding alerts, `Topic` names, explanations of why the answer is correct, etc.). - An `Action` is a way to modify either the `dealer` program or the bidding. The embedded domain-specific language I've built is entirely made out of diff --git a/make_pdf/Main.hs b/make_pdf/Main.hs index 025862e1..550f735b 100644 --- a/make_pdf/Main.hs +++ b/make_pdf/Main.hs @@ -49,9 +49,7 @@ main = let , Lampe.topic , TripleFourOne.topic ] - topics = [ MajorSuitRaises.topic - , ForcingOneNotrump.topic - , Jacoby2NT.topic + topics = [ Lebensohl.topic ] in do -- outputLatex returns a copy of the contents of the file it wrote, but we diff --git a/src/Bids/Lebensohl.hs b/src/Bids/Lebensohl.hs index e468c5a3..2751dac3 100644 --- a/src/Bids/Lebensohl.hs +++ b/src/Bids/Lebensohl.hs @@ -9,28 +9,41 @@ module Bids.Lebensohl( , b1No2C2D , b1No2C2H -- Actual lebensohl bids + , b1N2N3C , b1No2C3D , b1No2C3H , b1No2C3S , b1No2D2H , b1No2D2S + , b1No2D2N + , b1No2D2N3CP , b1No2D3C , b1No2D3H , b1No2D3S , b1No2H2S + , b1No2H2N + , b1No2H2N3CP + , b1No2H2N3C3D , b1No2H3C , b1No2H3D , b1No2H3S + , b1No2S2N + , b1No2S2N3CP + , b1No2S2N3C3D + , b1No2S2N3C3H , b1No2S3C , b1No2S3D , b1No2S3H + , b1NoBM2N -- For when the opponents show both majors ) where import Action(Action, withholdBid) import qualified Bids.OneNotrump as NT -import EDSL(minSuitLength, makeCall, longerThan, atLeastAsLong, pointRange, - forbid, balancedHand, hasStopper, alternatives, soundHolding) +import EDSL(minSuitLength, makeCall, makeAlertableCall, pointRange, forEach, + forbid, balancedHand, hasStopper, alternatives, soundHolding, + longerThan, atLeastAsLong) +import Output((.+)) import qualified Terminology as T @@ -61,32 +74,35 @@ b1No2C2H :: Action b1No2C2H = NT.b1N2H -- Signoffs -b1No2D2H :: Action -b1No2D2H = do +signoff_ :: Int -> T.Suit -> Action +signoff_ level suit = do NT.lessThanInvitational - pointRange 5 40 - minSuitLength T.Hearts 5 + pointRange 5 40 -- We should have at least half the points to compete + -- NOTE: if we're nonvul, we might compete with less than half the points if + -- we're extra shapely. That's hard to encode here because we don't know the + -- vulnerability yet, and annoying to code in a topic because we can't reuse + -- this function for that situation. Just skip it; users won't get to + -- practice but it's a rare situation that is hopefully obvious when it + -- occurs. + minSuitLength suit 5 + -- Have a good reason to bid this suit. + alternatives [soundHolding suit, minSuitLength suit 6] -- If you're 5-5 in the majors, pick the better suit. I'm too lazy to add -- something to the EDSL to figure out which suit is better, so avoid it. - T.Hearts `longerThan` T.Clubs - T.Hearts `longerThan` T.Diamonds - T.Hearts `longerThan` T.Spades - makeCall $ T.Bid 2 T.Hearts + forEach (filter (/= suit) T.allSuits) (suit `longerThan`) + makeCall $ T.Bid level suit + + +b1No2D2H :: Action +b1No2D2H = signoff_ 2 T.Hearts b1No2D2S :: Action -b1No2D2S = do - NT.lessThanInvitational - pointRange 5 40 - minSuitLength T.Spades 5 - T.Spades `longerThan` T.Clubs - T.Spades `longerThan` T.Diamonds - T.Spades `longerThan` T.Hearts - makeCall $ T.Bid 2 T.Spades +b1No2D2S = signoff_ 2 T.Spades b1No2H2S :: Action -b1No2H2S = b1No2D2S +b1No2H2S = signoff_ 2 T.Spades primarySuit_ :: T.Suit -> Action @@ -159,3 +175,67 @@ b1No2D3S = gfWithSuit_ T.Spades T.Diamonds b1No2H3S :: Action b1No2H3S = gfWithSuit_ T.Spades T.Hearts + + +-- Signoffs that you couldn't bid at the 2 level +b1No2D2N3CP :: Action +b1No2D2N3CP = do + withholdBid $ signoff_ 8 T.Clubs + makeCall T.Pass + +b1No2H2N3CP :: Action +b1No2H2N3CP = do + withholdBid $ signoff_ 8 T.Clubs + makeCall T.Pass + +b1No2S2N3CP :: Action +b1No2S2N3CP = do + withholdBid $ signoff_ 8 T.Clubs + makeCall T.Pass + +b1No2H2N3C3D :: Action +b1No2H2N3C3D = signoff_ 3 T.Diamonds + +b1No2S2N3C3D :: Action +b1No2S2N3C3D = signoff_ 3 T.Diamonds + +b1No2S2N3C3H :: Action +b1No2S2N3C3H = signoff_ 3 T.Hearts + + +-- Time for the actual lebensohl relays! +b1N2N3C :: Action +b1N2N3C = makeAlertableCall (T.Bid 3 T.Clubs) "relay completed" + + +b1No2D2N :: Action +b1No2D2N = do + alternatives [ b1No2D2N3CP + ] + makeAlertableCall (T.Bid 2 T.Notrump) ("relay to " .+ T.Bid 3 T.Clubs) + + +b1No2H2N :: Action +b1No2H2N = do + alternatives [ b1No2H2N3CP + , b1No2H2N3C3D + ] + makeAlertableCall (T.Bid 2 T.Notrump) ("relay to " .+ T.Bid 3 T.Clubs) + + +b1No2S2N :: Action +b1No2S2N = do + alternatives [ b1No2S2N3CP + , b1No2S2N3C3D + , b1No2S2N3C3H + ] + makeAlertableCall (T.Bid 2 T.Notrump) ("relay to " .+ T.Bid 3 T.Clubs) + + +-- Have a special one for when the opponents show both majors +b1NoBM2N :: Action +b1NoBM2N = do + alternatives [ b1No2S2N3CP + , b1No2S2N3C3D + ] + makeAlertableCall (T.Bid 2 T.Notrump) ("relay to " .+ T.Bid 3 T.Clubs) diff --git a/src/Topics/Lebensohl.hs b/src/Topics/Lebensohl.hs index 136f93db..963f306f 100644 --- a/src/Topics/Lebensohl.hs +++ b/src/Topics/Lebensohl.hs @@ -2,14 +2,15 @@ module Topics.Lebensohl(topic) where import Control.Monad(join) +import Action(extractLastCall, withholdBid) import qualified Bids.Cappelletti as Capp import qualified Bids.DONT as DONT import qualified Bids.Lebensohl as Leb import qualified Bids.Meckwell as MW import qualified Bids.NaturalOneNotrumpDefense as Nat import CommonBids(setOpener) ---import EDSL(makePass, pointRange, suitLength, maxSuitLength, forEach) -import Output((.+)) +import EDSL(makePass) +import Output(Description, (.+)) import Situation(situation, (<~)) import qualified Terminology as T import Topic(Topic, wrap, Situations, makeTopic) @@ -58,8 +59,8 @@ ignoreOpps = let <~ T.allVulnerabilities -signoff :: Situations -signoff = let +signoff2 :: Situations +signoff2 = let sit (overcall, response) = let action = do setOpener T.North @@ -70,7 +71,7 @@ signoff = let "with the auction. We're so weak we don't even want to invite " .+ "to game, but we do have enough strength to suspect this is " .+ "our contract. Bid our suit at the 2 level, as signoff." - in situation "snof" action response explanation + in situation "so2" action response explanation in wrap $ return sit <~ [ (DONT.b1No2D, Leb.b1No2D2H) , (DONT.b1No2D, Leb.b1No2D2S) @@ -130,15 +131,108 @@ gameForce = let <~ T.allVulnerabilities +signoff3 :: Situations +signoff3 = let + sit (overcall, relay, responses) dlr vul = let + inner response = let + action = do + setOpener T.North + Leb.b1N + _ <-overcall + withholdBid response + responseDescription :: Description + responseDescription = + if (T.removeAlert . extractLastCall $ response) == T.Pass + then "pass" .+ "" + else "bid " .+ response + explanation = + "Partner opened a strong " .+ Leb.b1N .+ ", and RHO " .+ + "interfered with the auction. We're less-than-invitational, " .+ + "and just want to sign off in partscore. However, we can't " .+ + "do that at the 2 level any more. Make a lebensohl relay, " .+ + "planning to " .+ responseDescription .+ " afterwards." + in situation "so3" action relay explanation dlr vul + in return inner <~ responses + in + wrap . join $ return sit + <~ [ (Nat.b1No2D, Leb.b1No2D2N, + [Leb.b1No2D2N3CP]) + , (Nat.b1No2H, Leb.b1No2H2N, + [Leb.b1No2H2N3CP, Leb.b1No2H2N3C3D]) + , (Nat.b1No2S, Leb.b1No2S2N, + [Leb.b1No2S2N3CP, Leb.b1No2S2N3C3D, Leb.b1No2S2N3C3H]) + , (DONT.b1No2D, Leb.b1No2D2N, + [Leb.b1No2D2N3CP]) + , (DONT.b1No2H, Leb.b1No2H2N, + -- Don't bid either major when the opponents have shown both + [Leb.b1No2H2N3CP, Leb.b1No2H2N3C3D]) + , (DONT.b1No2S, Leb.b1No2S2N, + [Leb.b1No2S2N3CP, Leb.b1No2S2N3C3D, Leb.b1No2S2N3C3H]) + , (MW.b1No2D, Leb.b1No2D2N, + [Leb.b1No2D2N3CP]) + , (MW.b1No2H, Leb.b1No2H2N, + [Leb.b1No2H2N3CP, Leb.b1No2H2N3C3D]) + , (MW.b1No2S, Leb.b1No2S2N, + [Leb.b1No2S2N3CP, Leb.b1No2S2N3C3D, Leb.b1No2S2N3C3H]) + -- Again, don't bid a major when RHO has them both. 3D should be + -- natural and not a cue bid, because you'd never want to have a + -- Stayman-like bid when RHO has shown both majors. + , (Capp.b1No2D, Leb.b1NoBM2N, + [Leb.b1No2D2N3CP, Leb.b1No2H2N3C3D]) + , (Capp.b1No2H, Leb.b1No2H2N, + [Leb.b1No2H2N3CP, Leb.b1No2H2N3C3D]) + , (Capp.b1No2S, Leb.b1No2S2N, + [Leb.b1No2S2N3CP, Leb.b1No2S2N3C3D, Leb.b1No2S2N3C3H]) + ] + -- East should be an unpassed hand to interfere. + <~ [T.North, T.South, T.West] + <~ T.allVulnerabilities + + +completeRelay :: Situations +completeRelay = let + sit (overcall, relay) = let + action = do + setOpener T.South + Leb.b1N + _ <- overcall + _ <-relay + makePass -- TODO: prevent RHO from raising LHO's suit + explanation = + "We opened a strong " .+ Leb.b1N .+ ", and LHO interfered " .+ + "with the auction. Partner made a lebensohl bid, and we " .+ + "should complete the relay to see what they do next. Partner " .+ + "is captain of the auction, and knows where we're going." + in situation "relay" action Leb.b1N2N3C explanation + in + wrap $ return sit + <~ [ (Nat.b1No2D, Leb.b1No2D2N) + , (Nat.b1No2H, Leb.b1No2H2N) + , (Nat.b1No2S, Leb.b1No2S2N) + , (DONT.b1No2D, Leb.b1No2D2N) + , (DONT.b1No2H, Leb.b1NoBM2N) + , (DONT.b1No2S, Leb.b1No2S2N) + , (MW.b1No2D, Leb.b1No2D2N) + , (MW.b1No2H, Leb.b1No2H2N) + , (MW.b1No2S, Leb.b1No2S2N) + -- Again, don't bid a major when RHO has them both. 3D should be + -- natural and not a cue bid, because you'd never want to have a + -- Stayman-like bid when RHO has shown both majors. + , (Capp.b1No2D, Leb.b1NoBM2N) + , (Capp.b1No2H, Leb.b1No2H2N) + , (Capp.b1No2S, Leb.b1No2S2N) + ] + -- West should be an unpassed hand to interfere. + <~ [T.North, T.South, T.East] + <~ T.allVulnerabilities + -- TODO: --- natural GF bids at the 3 level -- jump to 3N -- relay to 3N (answer should be 2N planning to rebid 3N) -- cue bid for Stayman -- relay to cue bid (answer should be 2N planning to rebid the cue) --- complete the relay --- Texas transfers over interference (in the Texas Transfers topic) --- make the opponents sometimes use natural overcalls +-- pass after relay and signoff +-- pass or bid game after relay and invite topic :: Topic @@ -147,6 +241,8 @@ topic = makeTopic "leb1N" situations where situations = wrap [ ignoreOpps - , signoff + , signoff2 + , signoff3 , gameForce + , completeRelay ]