The bdeal is a program that generates (randomly) card distributions that meets given requirements.

bdeal is developed by Piotr Beling and it is a part of the Bridge Calculator.

The main futures of the program are:
• it is scriptable in the Lua,
• it uses multiple threads to generates and filters distributions of cards,
• it has built-in, fast double-dummy solver,
• it has console-based, shell-script-friendly user interface.

# Quick start examples¶

## The chance to win 3NT¶

Suppose that your partner (S) opened 1NT (classical, 16-18 hcp) and you (N) have ♠K42 ♥QT2 ♦K98 ♣J653.

To check how many trick, in average, S can take in NT and what is a chance to win 3NT, call:

```\$ bdeal nt_script.lua
```

Where `nt_script.lua` is a name of file with Lua script from which bdeal will read all details. It may have the following content:

```conf = {
N="K42.QT2.K98.J653",    -- fix N hand
num=100                  -- finish after accepting 100 deals
}

-- accept only the deals in which S has 1NT opening:
function filter()
return S:nt(16, 18)
end

-- for each accepted deal, calculate number of tricks in NT by S:
function stats()
local t = tricks(S, "NT")
count("NT by S, number of tricks", t)
count("chance to win 3NT by S", t >= 9)
end
```

See Lua scripts for details about writing scripts.

## No one can make 7 tricks¶

This script accept only deals in which it is impossible to win any contract (after optimal defence):

```conf = { num = 1 }  -- we want to find only one distribution

function filter()
for i1, s in pairs({ 'c', 'd', 'h', 's', 'nt' }) do
for i2, p in pairs({ 'n', 'e', 'w', 's' }) do
if cantake(7, p, s) then
return false
end
end
end
return true
end
```

Warning

It may take a long time (even few hours) before it accepts any distribution.

## The best lead against 3NT¶

Supposed that N opened 1NT (15-18 hcp) and S bid 3NT (10-12 hcp, without major 4). E is on lead and has ♠K873 ♥J1076 ♦A87 ♣Q7.

This script measures the average numbers of tricks E-W can take and the chances to defeat the contract (3NT), after each lead:

```conf = { num=2000, E="K873.J1076.A87.Q7" }

function filter()
return N:nt(15, 18) and S:nt(10, 12) and S:spades() < 4 and S:hearts() < 4
end

function stats()
for c in E:cards() do  -- for each card c in the hand E:
local t = tricks(N, "NT", c) -- tricks to take by N-S
count("E-W tricks after " .. tostring(c), 13 - t)
count("chance to defeat after " .. tostring(c), t < 9)
end
end
```

# Invocation¶

bdeal is called like this:

```\$ bdeal [options] <file names...>
```

where <file names…> are the (zero or more) names of files with Lua scripts.

The bdeal has several options:

`-r``, ``--noremarks`

Don’t print remarks provided by calls of remark function.

`-s``, ``--nostats`

Don’t print statistics for individual deals.

`-f`` <none|NESW|short|full|PBN|PBNf>``, ``--format`` <none|NESW|short|full|PBN|PBNf>`

Output format, one of: none (do not print), NESW, short (used by default), full, PBN, PBNf (PBN file/full).

`-W`` <cards>``, ``--west`` <cards>`

Fixed cards in WEST hand.

<cards> should has a format like in examples:
“AKQJ.AK65.642.94”
(AKQJ of spades, AK65 of hearts, 642 of diamonds, 94 of clubs)
“63.6.9.AQ”
(only 6 cards are fixed, rest will be chosen randomly)
“Q7.8543..94”
(8 cards are fixed, 0 in diamonds)
“..K.”
(the king of diamonds + 12 random cards)
`-S`` <cards>``, ``--south`` <cards>`

Fixed cards in SOUTH hand.

`-E`` <cards>``, ``--east`` <cards>`

Fixed cards in EAST hand.

`-N`` <cards>``, ``--north`` <cards>`

Fixed cards in NORTH hand.

`-n`` <number of hands>``, ``--num`` <number of hands>`

Number of hands to deal (10 by default).

`-j`` <number of threads>``, ``--jobs`` <number of threads>`

Number of threads to use (by default equals to number of available CPU cores).

If argument is non-positive, it will be increased by number of available CPU cores (0 to use all cores, -1 to use all cores except one).

`-h``, ``--help`

Displays usage information and exits.

`--``, ``--ignore_rest`

Ignores the rest of the labeled arguments following this flag.

`--version`

Displays version information and exits.

# Lua scripts¶

Bdeal accepts scripts in Lua language.

The scripts can include (each element is optional):

If more than one script are given, they are read sequentially (from left to right), and each can overwrite abovementioned elements (i.e. if two scripts provide `filter()` function, the one from script given later will be used).

## Configuration¶

Some configuration options can be put in Lua script file, in `conf` table or in `conf` function that returns a table, for instance:

```-- N has king of diamond, W has AQ of spades and 9 of clubs, use 2 threads:
conf = { N="..K.", W="AQ...9", jobs=2 }
```

The following options (keys in the `conf` table) are allowed: `N`, `E`, `S`, `W`, `format`, `num`, `jobs`. See Invocation for description of the options.

## Filtering¶

Script can contain `filter` function which describes deal constraints. The function can accept or refuse deal described by the variables `N`, `E`, `S`, `W` and should return:
• `true` to accept the deal,
• `false` to refuse it,
• a number in range [0, 1], which will be interpreted as probability of accepting the deal.

Each of the `N`, `E`, `S`, `W` represents the sets of cards held by the respective hands.

Functions and operators described in next sections can be used to examine the properties of the hands.

If all given scripts do not include `filter()` function, all distributions are accepted.

Examples:
• accept deals in which E has 12 or more honour points, and more than 2 points in diamonds:

```function filter()
return E:hcp() >= 12 and E:D():hcp() > 2
end
```
• accept deals in which W opens 1 spade and should have 5 or more spades, and 11 or more honour points (but we know that W sometimes, with 10% probability, opens with 4 strong spades – but only if he has less than 5 hearts):

```function filter()
if W:hcp() < 11 then    -- W does not have 11 points?
return false        -- refuse
end
-- here, W has 11 or more points
if s >= 5 then  -- has 5 spades?
return true -- accept
end
if s == 4 and W:S():hcp() >= 6 and W:hearts() < 5 then  -- 4 strong spades without 5 hearts?
return 0.10     -- accept with 10% probability
end
return false    -- everything else is refused, this line can be omitted (if filter returns no value, the deal will be refused)
end
```

## Statistics¶

Script can contain `stats` function which is called for each deal accepted by `filter` function. The `stats` function also has access to `N`, `E`, `S` and `W` variables.

Typically, this function calls one or more time `count` function, which can collect series of numbers and calculates some statistics (average and others).

Examples:
• checking how many spades and honour points does W have in average:

```function stats()
count("honour points in W's hand", W:hcp())
end
```

## Cards set operations (operators)¶

Let `s1` and `s2` be sets of cards. The following operators are available:

`s1 + s2`
Sum of the sets, for example: `E:S() + E:H()` represents E majors.
`s1 * s2`
Product of the sets, for example: `W * C.new("AS AH")` represents W major aces.
`s1 - s2`
Difference of the sets, for example: `N - N:S()` represents N cards without spades.
`-s1`
Complement of the set, for example: `-S` represents all 52-13 cards not included in S’s hand.
`#s1`

Number of cards in `s1`, same as `count(s1)` or `s1:count()`.

For example: `#N:S()` – a number of spades cards in N’s hand.

`s1 ^ s2`
xor, the set of cards which are in exactly one set `s1` or `s2`.
`s1 == s2`
`true` only if sets `s1` and `s2` are equals.
`s1 ~= s2`
`true` only if sets `s1` and `s2` are not equals.
`s1 <= s2`
`true` only if set `s1` is included in `s2`.
`s1 < s2`
`true` only if set `s1` is included in but not equal to `s2`.

## Functions which operates on sets of cards¶

Note

Each function, excluding `new`, can be called as `C.function_name(arg1, arg2, ..., argN)` or `arg1:function_name(arg2, ..., argN)`.

For example `C.hcp(N)` is equal to `N:hcp()`.

For sets of cards `c`, `c1`, `c2`, …, a function `f`, and a string `str`:

`C.``new`(str)

Construct a set of cards described by the string `str` (note that there is upper-case `C` here). Accepts many formats.

Example usage (all represent the same set of 10 cards): `C.new("AK87.975..QT9")`, `C.new("AK87 975 - QT9")`, `C.new("SAK87 H975 CQT9")`, `C.new("AK87s 975h QT9c")`

`count`(c)

A number of cards in the given cards set `c`, same as `#c`.

`spades`(c)
`Scount`(c)

A number of spades cards in set of cards `c`.

`hearts`(c)
`Hcount`(c)

A number of hearts cards in set of cards `c`.

`diamonds`(c)
`Dcount`(c)

A number of diamonds cards in set of cards `c`.

`clubs`(c)
`Ccount`(c)

A number of clubs cards in set of cards `c`.

`S`(c)

Subset of `c` which includes only spades.

`H`(c)

Subset of `c` which includes only hearts.

`D`(c)

Subset of `c` which includes only diamonds.

`C`(c)

Subset of `c` which includes only clubs.

`get`(c, arg)

If `arg` is a string, return the subset of `c` which includes only cards of suit pointed by `arg` (which can equal to “S”, “H”, “D” or “C”).

If `arg` is a positive integer, return `arg`-th lowest card from `c`. Cards are numbered from `0` to `#c-1`, clubs have number from `0` to `#c:C()-1`, diamonds from `#c:C()` to `#c:C()+#c:D()-1`, and so on. If `arg` is out of range, an empty set is returned.

If `arg` is a negative integer, return `#c+arg` lowest (`-arg` highest) card from `c`.

`cards`(c)

Iterator over cards included in `c`. Each card is represented as a one-element set of cards.

Example:

```for x in N:cards() do   -- for each card x handled by N:
-- do something with card x
end
```
`hcp`(c)
`miltons`(c)

Honour points (4 for Ace, 3 for King, 2 for Queen, 1 for Jack) in the given cards set `c`.

`controls`(c)

Controls (2 for Ace, 1 for King) in the given cards set `c`.

`points`(c, wAce, wKing, ...)

Calculate `wAce` * Aces + `wKing` * Kings + …, where Aces, Kings, … are the numbers of aces, kings, … in `c`.

Examples:
• `points(N, 4, 3, 2, 1)` equals to `hcp(N)`,
• `points(E, 1, 1, 1, 1)` equals to the number of figures owned by E.
`balanced`(c)

Logic value which is `true` only if `c` is balanced.

`semiBalanced`(c)

Logic value which is `true` only if `c` includes minimum 2 cards in all suits, maximum 5 cards in each major and maximum 6 cards in each minor.

`union`(c1, c2, ...)

Union of the sets, same as `c1 + c2 + ...`

`product`(c1, c2, ...)

Product of the sets, same as `c1 * c2 * ...`

`complement`(c)

Complement of the set `c`, same as `-c`.

`xor`(c1, c2, ...)

Same as `c1 ^ c2 ^ ...`

`shape`(c)

Return 4 numbers: `spades(c), hearts(c), diamonds(c), clubs(c)` (in the given order).

`shape`(c, f)

Calculate and return `f(spades(c), hearts(c), diamonds(c), clubs(c))`.

For example `shape(S, function(s, h, d, c) return s == 5 and h == 5; end)` gives `true` only if `S` has 5-5 in majors.

`pattern`(c)

Return 4 numbers: `spades(c), hearts(c), diamonds(c), clubs(c)` (in non-increasing order, from highest to lowest).

`pattern`(c, f)

Return `f(c1, c2, c3, c4)`, where `c1, c2, c3, c4 = pattern(c)`.

For example `pattern(S, function(a, b, c, d) return a == 5 and b == 5; end)` gives `true` only if `S` has any 5-5.

`hcp_in_range`(c, from, to)

Return `true` if `hcp(c)` is in the range [`from`, `to`], and `false` if it is not.

`nt`(c, from, to)

Calculate logic value which is `true` only if `c` is balanced and `hcp(c)` is in the range [`from`, `to`].

Tip

Most of the commands can be called with more than one argument and than they return more than one result (one per argument). In such case `command(a, b, ...)` is equal to `command(a), command(b), ...`. For example you can write:`EpS, EpH = C.hcp(E:S(), E:H())` to obtain number of E honour points in spades and hearts.

## Functions useful for doing statistical research¶

`count`(str, n1, n2, ...)

Add to the counter named `str` values `n1`, `n2`, …

Each value (`n1`, `n2`, …) can be a number, a boolean (1 is added if the value is `true` and 0 when it is `false`) or `nil` (then value is ignored).

Example:

```count("number of spades in E's hand", E:spades())
```
`remark`(arg1, arg2, ...)

Attach a remark to the deal considered. The remark consists with concatenation of arguments, which are converted to strings. Remarks are printed next to each deal.

Example:

```remark("E can take ", tricks(E, "NT"), " tricks in NT.")
```
`note`(arg1, arg2, ...)

Similar to `remark(arg1, arg2, ...)` but puts spaces between each pair of arguments.

Example:

```note("E can take", tricks(E, "NT"), "tricks in NT.")
```

## Functions that use double dummy solver¶

`tricks`(declarer, game[, cmds])

Return the number of tricks that `declarer` can take when a trump suit is given by `game`, and a beginning of play is described by `cmds`. If game cannot begin from `cmds`, return `nil`.

Parameters: declarer – one of `N` (or `'N'`), `E` (or `'E'`), `S` (or `'S'`), `W` (or `'W'`), lower cases are also allowed game – shows type of the game, is one of: `'NT'` (no trump), `'S'` (spades), `'H'` (hearts), `'D'` (diamonds), `'C'` (clubs), lower cases are also allowed. cmds – (optional) fixes beginning of the play; if it is not possible, `tricks` returns `nil` number or `nil` (if the game cannot begin from `cmds`)

Example:

```tricks(N, "NT", "AD x QD")
```

It returns number of tricks that can be taken by N in no-trump game, after ace of diamond lead, discarding the smallest diamond by the dummy and queen of diamond by the right hand opponent. `nil` will be returned if: N has no ace of diamond, the dummy has no diamonds at all, or RHO has no queen of diamond.

Since `count` ignores `nil`, it is save to write:

```count("NT by N after AD x QD", tricks(N, "NT", "AD x QD"))
```
`cantake`(target, declarer, game[, cmds])

Check if `declarer` can take `target` tricks when a trump suit is given by `game`, and a beginning of play is described by `cmds`.

Parameters: target – number of tricks to take declarer – one of `N` (or `'N'`), `E` (or `'E'`), `S` (or `'S'`), `W` (or `'W'`), lower cases are also allowed game – shows type of the game, is one of: `'NT'` (no trump), `'S'` (spades), `'H'` (hearts), `'D'` (diamonds), `'C'` (clubs), lower cases are also allowed. cmds – (optional) fixes beginning of the play, if it is not possible, `cantake` returns `nil` boolean or `nil` (if the game cannot begin from `cmds`)

Example:

```count("chance to win 3NT by N", cantake(9, N, "NT"))
```