Add list-negative-doubles script to help make sure that I don't change expectations when moving to a new NegativeDouble rule
Add name properties for Opened and HasBid preconditions
Rewrite NegativeDouble to use much less code.

This should not change behavior.  I used the list-negative-doubles script to verify.

Before:
    '1C 1D': [points >= 6, hearts >= 4, spades >= 4], # NegativeDoubleOfOneDiamondOverOneClub
    '1C 1H': [points >= 6, spades == 4], # NegativeDoubleOfOneHeartOverOneClub
    '1C 1S': [points >= 6, diamonds >= 3, hearts >= 4], # NegativeDoubleOfOneSpadeOverOneClub
    '1C 2D': [points >= 8, hearts >= 4, spades >= 4], # NegativeDoubleOfTwoDiamondsOverOneClub
    '1C 2H': [points >= 8, diamonds >= 3, spades >= 4], # NegativeDoubleOfTwoHeartsOverOneClub
    '1C 2S': [points >= 8, diamonds >= 3, hearts >= 4], # NegativeDoubleOfTwoSpadesOverOneClub
    '1D 1H': [points >= 6, spades == 4], # NegativeDoubleOfOneHeartOverOneDiamond
    '1D 1S': [points >= 6, clubs >= 3, hearts >= 4], # NegativeDoubleOfOneSpadeOverOneDiamond
    '1D 2C': [points >= 8, hearts >= 4, spades >= 4], # NegativeDoubleOfTwoClubsOverOneDiamond
    '1D 2H': [points >= 8, clubs >= 3, spades >= 4], # NegativeDoubleOfTwoHeartsOverOneDiamond
    '1D 2S': [points >= 8, clubs >= 3, hearts >= 4], # NegativeDoubleOfTwoSpadesOverOneDiamond
    '1H 1S': [points >= 6, clubs >= 3, diamonds >= 3], # NegativeDoubleOfOneSpadeOverOneHeart
    '1H 2C': [points >= 8, diamonds >= 3, spades >= 4], # NegativeDoubleOfTwoClubsOverOneHeart
    '1H 2D': [points >= 8, clubs >= 3, spades >= 4], # NegativeDoubleOfTwoDiamondsOverOneHeart
    '1H 2S': [points >= 8, clubs >= 3, diamonds >= 3], # NegativeDoubleOfTwoSpadesOverOneHeart
    '1S 2C': [points >= 8, diamonds >= 3, hearts >= 4], # NegativeDoubleOfTwoClubsOverOneSpade
    '1S 2D': [points >= 8, clubs >= 3, hearts >= 4], # NegativeDoubleOfTwoDiamondsOverOneSpades
    '1S 2H': [points >= 8, clubs >= 3, diamonds >= 3], # NegativeDoubleOfTwoHeartsOverOneSpade

After:
    '1C 1D': [And(hearts >= 4, spades >= 4), points >= 6], # OneLevelNegativeDouble
    '1C 1H': [spades == 4, points >= 6], # OneLevelNegativeDouble
    '1C 1S': [And(diamonds >= 3, hearts >= 4), points >= 6], # OneLevelNegativeDouble
    '1C 2D': [And(hearts >= 4, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1C 2H': [And(diamonds >= 3, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1C 2S': [And(diamonds >= 3, hearts >= 4), points >= 8], # TwoLevelNegativeDouble
    '1D 1H': [spades == 4, points >= 6], # OneLevelNegativeDouble
    '1D 1S': [And(clubs >= 3, hearts >= 4), points >= 6], # OneLevelNegativeDouble
    '1D 2C': [And(hearts >= 4, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1D 2H': [And(clubs >= 3, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1D 2S': [And(clubs >= 3, hearts >= 4), points >= 8], # TwoLevelNegativeDouble
    '1H 1S': [And(clubs >= 3, diamonds >= 3), points >= 6], # OneLevelNegativeDouble
    '1H 2C': [And(diamonds >= 3, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1H 2D': [And(clubs >= 3, spades >= 4), points >= 8], # TwoLevelNegativeDouble
    '1H 2S': [And(clubs >= 3, diamonds >= 3), points >= 8], # TwoLevelNegativeDouble
    '1S 2C': [And(diamonds >= 3, hearts >= 4), points >= 8], # TwoLevelNegativeDouble
    '1S 2D': [And(clubs >= 3, hearts >= 4), points >= 8], # TwoLevelNegativeDouble
    '1S 2H': [And(clubs >= 3, diamonds >= 3), points >= 8], # TwoLevelNegativeDouble