using System;
namespace Analizator9000
{
///
/// Tuple describing contract parameters.
///
public class Contract: IEquatable
{
///
/// Denomination: non-trumps
///
public const int DENOMINATION_NONTRUMP = 4;
///
/// Denomination: spades
///
public const int DENOMINATION_SPADES = 3;
///
/// Denomination: hearts
///
public const int DENOMINATION_HEARTS = 2;
///
/// Denomination: diamonds
///
public const int DENOMINATION_DIAMONDS = 1;
///
/// Denomination: clubs
///
public const int DENOMINATION_CLUBS = 0;
///
/// Declarer: North
///
public const int DECLARER_NORTH = 0;
///
/// Declarer: East
///
public const int DECLARER_EAST = 1;
///
/// Declarer: South
///
public const int DECLARER_SOUTH = 2;
///
/// Declarer: West
///
public const int DECLARER_WEST = 3;
///
/// Vulnerability: none
///
public const int VULNERABLE_NONE = 0;
///
/// Vulnerability: all
///
public const int VULNERABLE_BOTH = 1;
///
/// Vulnerability: NS
///
public const int VULNERABLE_NS = 2;
///
/// Vulnerability: EW
///
public const int VULNERABLE_EW = 3;
///
/// Level of the contract.
///
public int Level;
///
/// Trump denomination, in numeric format.
///
///
public int Denomination;
///
/// Contract without double
///
public const int MODIFIERS_NONE = 0;
///
/// Contract doubled
///
public const int MODIFIERS_DOUBLE = 1;
///
/// Contract redoubled
///
public const int MODIFIERS_REDOUBLE = 2;
///
/// Modifiers - double or redouble.
///
public int Modifiers;
///
/// Declaring player, in numeric format.
///
///
public int Declarer;
///
/// Number of occurrences of the contract within board traveller.
///
public int Frequency;
///
/// Position of the contract in boards traveller table.
///
public int TableRow;
///
/// Constructor for contract without full information (just general trick-taking in a denomination).
///
/// Trump denomination.
/// Declaring player.
public Contract(int denom, int decl)
{
this.Denomination = denom;
this.Declarer = decl;
}
///
/// Constructor with full contract information.
///
/// Contract level.
/// Trump denomination.
/// Declaring player.
/// Modifiers: 1 = X, 2 = XX
/// Traveller frequency
/// Traveller row index
public Contract(int level, int denom, int decl, int modifiers = 0, int frequency = 0, int tableRow = 0)
{
this.Frequency = frequency;
this.Level = level;
this.Denomination = denom;
this.Declarer = decl;
this.Modifiers = modifiers;
this.TableRow = tableRow;
}
///
/// IEquatable method for comparing (checking equality) of two tuples.
///
/// Tuple to compare to.
/// TRUE if both tuple components are equal.
public bool Equals(Contract other)
{
return this.Denomination == other.Denomination && this.Declarer == other.Declarer &&
this.Level == other.Level && this.Modifiers == other.Modifiers && this.Frequency == other.Frequency && this.TableRow == other.TableRow;
}
///
/// Method used in object comparisons, taking the Equals() method into account.
///
///
/// Object hash
public override int GetHashCode()
{
int hash = this.Denomination;
hash *= 10;
hash += this.Declarer;
hash *= 10;
hash += this.Level;
hash *= 10;
hash += this.Modifiers;
hash *= 10;
hash += this.Frequency;
hash *= 10;
hash += this.TableRow;
return hash;
}
///
/// Calculates score for contract result
///
/// BCalc output result
/// Vulnerability for the deal
/// Score for NS pair
public int getScore(BCalcResult result, int vulnerability)
{
// All PASS.
if (this.Level == 0)
{
return 0;
}
if (BCalcWrapper.table[this.Declarer] != result.declarer)
{
throw new Exception("Declarer mismatch!");
}
// Determining vulnerability of the contract
bool vulnerable = (vulnerability == Contract.VULNERABLE_BOTH)
|| (vulnerability == Contract.VULNERABLE_EW && this.Declarer == Contract.DECLARER_EAST)
|| (vulnerability == Contract.VULNERABLE_EW && this.Declarer == Contract.DECLARER_WEST)
|| (vulnerability == Contract.VULNERABLE_NS && this.Declarer == Contract.DECLARER_NORTH)
|| (vulnerability == Contract.VULNERABLE_NS && this.Declarer == Contract.DECLARER_SOUTH);
int score = 0;
// Contract not made
if (this.Level + 6 > result.tricks)
{
int undertricks = this.Level + 6 - result.tricks;
if (this.Modifiers < 1) // undoubled undertricks
{
score = vulnerable ? -100 : -50;
score *= undertricks;
}
else // (re-)doubled undertricks
{
do
{
if (undertricks == 1) // first undertrick: 100 non-vul, 200 vul
{
score -= vulnerable ? 200 : 100;
}
else
{
if (undertricks <= 3 && !vulnerable) // second non-vul undertrick: 200
{
score -= 200;
}
else // further undertricks: 300
{
score -= 300;
}
}
undertricks--;
}
while (undertricks > 0);
score *= this.Modifiers; // redouble doubles the score
}
}
else // Contract made, yay!
{
int parTricks = this.Level;
do
{
if (this.Denomination == Contract.DENOMINATION_NONTRUMP && parTricks == 1) // first non-trump trick: 40
{
score += 40;
}
else // other tricks
{
switch (this.Denomination)
{
case Contract.DENOMINATION_NONTRUMP:
case Contract.DENOMINATION_SPADES:
case Contract.DENOMINATION_HEARTS:
score += 30;
break;
case Contract.DENOMINATION_DIAMONDS:
case Contract.DENOMINATION_CLUBS:
score += 20;
break;
}
}
parTricks--;
}
while (parTricks > 0);
if (this.Modifiers > 0) // (re-)doubled tricks
{
score *= this.Modifiers * 2;
}
score += (score >= 100) ? (vulnerable ? 500 : 300) : 50; // game premium
if (this.Level == 7) // grand slam premium
{
score += vulnerable ? 1500 : 1000;
}
else if (this.Level == 6) // small slam premium
{
score += vulnerable ? 750 : 500;
}
score += this.Modifiers * 50; // failed (re-)double premium
int overtricks = result.tricks - this.Level - 6;
score += this.Modifiers > 0
? (vulnerable ? 200 : 100) * this.Modifiers * overtricks // (re-)double overtricks: 100/200/200/400
: overtricks * ((this.Denomination == Contract.DENOMINATION_CLUBS || this.Denomination == Contract.DENOMINATION_DIAMONDS) ? 20 : 30); // undoubled overtricks
}
// If EW played the board, the score is shifted to the other side
if (this.Declarer == Contract.DECLARER_WEST || this.Declarer == Contract.DECLARER_EAST)
{
score = -score;
}
return score;
}
}
}