Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lebensohl: Situations to sign off at the 3 level after a relay #102

Merged
merged 7 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions make_pdf/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
118 changes: 99 additions & 19 deletions src/Bids/Lebensohl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
116 changes: 106 additions & 10 deletions src/Topics/Lebensohl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -147,6 +241,8 @@ topic = makeTopic
"leb1N" situations
where
situations = wrap [ ignoreOpps
, signoff
, signoff2
, signoff3
, gameForce
, completeRelay
]