#include <gtest/gtest.h>
#include <dds/dds.hpp>
#define R2 0x0004
#define R3 0x0008
#define R4 0x0010
#define R5 0x0020
#define R6 0x0040
#define R7 0x0080
#define R8 0x0100
#define R9 0x0200
#define RT 0x0400
#define RJ 0x0800
#define RQ 0x1000
#define RK 0x2000
#define RA 0x4000
class CalcParTest : public ::testing::Test
{
protected:
void SetUp() override
{
for (int h = 0; h < DDS_HANDS; h++) {
for (int s = 0; s < DDS_SUITS; s++) {
deal0_.cards[h][s] = holdings0_[s][h];
}
}
for (int h = 0; h < DDS_HANDS; h++) {
for (int s = 0; s < DDS_SUITS; s++) {
deal1_.cards[h][s] = holdings1_[s][h];
}
}
for (int h = 0; h < DDS_HANDS; h++) {
for (int s = 0; s < DDS_SUITS; s++) {
deal2_.cards[h][s] = holdings2_[s][h];
}
}
}
void TearDown() override
{
}
unsigned int holdings0_[4][4] = {
{RQ|RJ|R6, R8|R7|R3, RK|R5, RA|RT|R9|R4|R2}, {RK|R6|R5|R2, RJ|R9|R7, RT|R8|R3, RA|RQ|R4}, {RJ|R8|R5, RA|RT|R7|R6|R4, RK|RQ|R9, R3|R2}, {RT|R9|R8, RQ|R4, RA|R7|R6|R5|R2, RK|RJ|R3} };
unsigned int holdings1_[4][4] = {
{RA|RK|R9|R6, RQ|RJ|RT|R5|R4|R3|R2, 0, R8|R7}, {RK|RQ|R8, RT, RJ|R9|R7|R5|R4|R3, RA|R6|R2}, {RA|R9|R8, R6, RK|R7|R5|R3|R2, RQ|RJ|RT|R4}, {RK|R6|R3, RQ|RJ|R8|R2, R9|R4, RA|RT|R7|R5} };
unsigned int holdings2_[4][4] = {
{R7|R3, RQ|RT|R6, R5, RA|RK|RJ|R9|R8|R4|R2}, {RQ|RJ|RT, R8|R7|R6, RA|R9|R5|R4|R3|R2, RK}, {RA|RQ|R5|R4, RK|RJ|R9, R7|R6|R3|R2, RT|R8}, {RT|R7|R5|R2, RA|RQ|R8|R4, RK|R6, RJ|R9|R3} };
DdTableDeal deal0_;
DdTableDeal deal1_;
DdTableDeal deal2_;
const char* expected_par_score_[3][2] = {
{ "NS -110", "EW 110" },
{ "NS 100", "EW -100" },
{ "NS -300", "EW 300" }
};
const char* expected_par_contracts_[3][2] = {
{ "NS:EW 2S", "EW:EW 2S" },
{ "NS:EW 4Sx", "EW:EW 4Sx" },
{ "NS:NS 5Hx", "EW:NS 5Hx" }
};
int vulnerability_[3] = { 0, 2, 0 }; };
TEST_F(CalcParTest, BasicCalcParHand0)
{
DdTableResults table;
ParResults par;
int result = calc_par(deal0_, vulnerability_[0], &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT) << "calc_par should succeed";
EXPECT_STREQ(par.par_score[0], expected_par_score_[0][0]);
EXPECT_STREQ(par.par_score[1], expected_par_score_[0][1]);
EXPECT_STREQ(par.par_contracts_string[0], expected_par_contracts_[0][0]);
EXPECT_STREQ(par.par_contracts_string[1], expected_par_contracts_[0][1]);
}
TEST_F(CalcParTest, BasicCalcParHand1)
{
DdTableResults table;
ParResults par;
int result = calc_par(deal1_, vulnerability_[1], &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT);
EXPECT_STREQ(par.par_score[0], expected_par_score_[1][0]);
EXPECT_STREQ(par.par_score[1], expected_par_score_[1][1]);
EXPECT_STREQ(par.par_contracts_string[0], expected_par_contracts_[1][0]);
EXPECT_STREQ(par.par_contracts_string[1], expected_par_contracts_[1][1]);
}
TEST_F(CalcParTest, BasicCalcParHand2)
{
DdTableResults table;
ParResults par;
int result = calc_par(deal2_, vulnerability_[2], &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT);
EXPECT_STREQ(par.par_score[0], expected_par_score_[2][0]);
EXPECT_STREQ(par.par_score[1], expected_par_score_[2][1]);
EXPECT_STREQ(par.par_contracts_string[0], expected_par_contracts_[2][0]);
EXPECT_STREQ(par.par_contracts_string[1], expected_par_contracts_[2][1]);
}
TEST_F(CalcParTest, CalcParWithContext)
{
SolverContext ctx;
DdTableResults table;
ParResults par;
int result = calc_par(ctx, deal0_, vulnerability_[0], &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT);
EXPECT_STREQ(par.par_score[0], expected_par_score_[0][0]);
EXPECT_STREQ(par.par_score[1], expected_par_score_[0][1]);
EXPECT_STREQ(par.par_contracts_string[0], expected_par_contracts_[0][0]);
EXPECT_STREQ(par.par_contracts_string[1], expected_par_contracts_[0][1]);
}
TEST_F(CalcParTest, ContextReuseMultipleCalls)
{
SolverContext ctx;
DdTableResults table1;
ParResults par1;
int result1 = calc_par(ctx, deal0_, vulnerability_[0], &table1, &par1);
ASSERT_EQ(result1, RETURN_NO_FAULT);
EXPECT_STREQ(par1.par_score[0], expected_par_score_[0][0]);
DdTableResults table2;
ParResults par2;
int result2 = calc_par(ctx, deal1_, vulnerability_[1], &table2, &par2);
ASSERT_EQ(result2, RETURN_NO_FAULT);
EXPECT_STREQ(par2.par_score[0], expected_par_score_[1][0]);
DdTableResults table3;
ParResults par3;
int result3 = calc_par(ctx, deal2_, vulnerability_[2], &table3, &par3);
ASSERT_EQ(result3, RETURN_NO_FAULT);
EXPECT_STREQ(par3.par_score[0], expected_par_score_[2][0]);
}
TEST_F(CalcParTest, CalcParFromTableHand0)
{
DdTableResults table;
ParResults par_full;
int result1 = calc_par(deal0_, vulnerability_[0], &table, &par_full);
ASSERT_EQ(result1, RETURN_NO_FAULT);
ParResults par_from_table;
int result2 = calc_par_from_table(&table, vulnerability_[0], &par_from_table);
ASSERT_EQ(result2, RETURN_NO_FAULT);
EXPECT_STREQ(par_from_table.par_score[0], par_full.par_score[0]);
EXPECT_STREQ(par_from_table.par_score[1], par_full.par_score[1]);
EXPECT_STREQ(par_from_table.par_contracts_string[0], par_full.par_contracts_string[0]);
EXPECT_STREQ(par_from_table.par_contracts_string[1], par_full.par_contracts_string[1]);
}
TEST_F(CalcParTest, VulnerabilityVariations)
{
DdTableResults table;
ParResults par;
for (int vuln = 0; vuln <= 3; vuln++) {
int result = calc_par(deal0_, vuln, &table, &par);
EXPECT_EQ(result, RETURN_NO_FAULT)
<< "calc_par should succeed for vulnerability " << vuln;
EXPECT_NE(par.par_score[0][0], '\0') << "Par score NS should not be empty";
EXPECT_NE(par.par_score[1][0], '\0') << "Par score EW should not be empty";
}
}
TEST_F(CalcParTest, TableResultsPopulated)
{
DdTableResults table;
ParResults par;
int result = calc_par(deal0_, vulnerability_[0], &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT);
for (int strain = 0; strain < DDS_STRAINS; strain++) {
for (int hand = 0; hand < DDS_HANDS; hand++) {
int tricks = table.res_table[strain][hand];
EXPECT_GE(tricks, 0) << "Tricks should be >= 0";
EXPECT_LE(tricks, 13) << "Tricks should be <= 13";
}
}
}
TEST_F(CalcParTest, CalcParFromTableVulnerability)
{
DdTableResults table;
ParResults par_temp;
int result = calc_par(deal0_, 0, &table, &par_temp);
ASSERT_EQ(result, RETURN_NO_FAULT);
for (int vuln = 0; vuln <= 3; vuln++) {
ParResults par;
int res = calc_par_from_table(&table, vuln, &par);
EXPECT_EQ(res, RETURN_NO_FAULT)
<< "Should compute par for vulnerability " << vuln;
}
}
TEST_F(CalcParTest, InvalidVulnerability)
{
DdTableResults table;
ParResults par;
int result = calc_par(deal0_, -1, &table, &par);
EXPECT_TRUE(result == RETURN_NO_FAULT || result < 0)
<< "Function should return valid code for invalid vulnerability";
}
TEST_F(CalcParTest, EmptyDealHandling)
{
DdTableDeal empty_deal;
for (int h = 0; h < DDS_HANDS; h++) {
for (int s = 0; s < DDS_SUITS; s++) {
empty_deal.cards[h][s] = 0;
}
}
DdTableResults table;
ParResults par;
int result = calc_par(empty_deal, 0, &table, &par);
EXPECT_NE(result, RETURN_NO_FAULT)
<< "Empty deal should produce an error";
}
TEST_F(CalcParTest, ConsistencyCalcParVsFromTable)
{
for (int hand_idx = 0; hand_idx < 3; hand_idx++) {
DdTableDeal* deal;
if (hand_idx == 0) deal = &deal0_;
else if (hand_idx == 1) deal = &deal1_;
else deal = &deal2_;
DdTableResults table1;
ParResults par1;
int res1 = calc_par(*deal, vulnerability_[hand_idx], &table1, &par1);
ASSERT_EQ(res1, RETURN_NO_FAULT);
ParResults par2;
int res2 = calc_par_from_table(&table1, vulnerability_[hand_idx], &par2);
ASSERT_EQ(res2, RETURN_NO_FAULT);
EXPECT_STREQ(par1.par_score[0], par2.par_score[0])
<< "Hand " << hand_idx << " NS scores should match";
EXPECT_STREQ(par1.par_score[1], par2.par_score[1])
<< "Hand " << hand_idx << " EW scores should match";
EXPECT_STREQ(par1.par_contracts_string[0], par2.par_contracts_string[0])
<< "Hand " << hand_idx << " NS contracts should match";
EXPECT_STREQ(par1.par_contracts_string[1], par2.par_contracts_string[1])
<< "Hand " << hand_idx << " EW contracts should match";
}
}
TEST_F(CalcParTest, CalcParContextOverloadMatchesNonContext)
{
for (int hand_idx = 0; hand_idx < 3; hand_idx++) {
DdTableDeal* deal;
if (hand_idx == 0) deal = &deal0_;
else if (hand_idx == 1) deal = &deal1_;
else deal = &deal2_;
DdTableResults table_no_ctx;
ParResults par_no_ctx;
int res1 = calc_par(*deal, vulnerability_[hand_idx], &table_no_ctx, &par_no_ctx);
ASSERT_EQ(res1, RETURN_NO_FAULT) << "Non-context call failed for hand " << hand_idx;
SolverContext ctx;
DdTableResults table_with_ctx;
ParResults par_with_ctx;
int res2 = calc_par(ctx, *deal, vulnerability_[hand_idx], &table_with_ctx, &par_with_ctx);
ASSERT_EQ(res2, RETURN_NO_FAULT) << "Context call failed for hand " << hand_idx;
EXPECT_STREQ(par_no_ctx.par_score[0], par_with_ctx.par_score[0])
<< "Hand " << hand_idx << " NS par scores differ";
EXPECT_STREQ(par_no_ctx.par_score[1], par_with_ctx.par_score[1])
<< "Hand " << hand_idx << " EW par scores differ";
EXPECT_STREQ(par_no_ctx.par_contracts_string[0], par_with_ctx.par_contracts_string[0])
<< "Hand " << hand_idx << " NS contracts differ";
EXPECT_STREQ(par_no_ctx.par_contracts_string[1], par_with_ctx.par_contracts_string[1])
<< "Hand " << hand_idx << " EW contracts differ";
}
}
TEST_F(CalcParTest, ContextReusePerformance)
{
SolverContext ctx;
const int num_iterations = 10;
for (int i = 0; i < num_iterations; i++) {
DdTableResults table;
ParResults par;
DdTableDeal* deal = (i % 2 == 0) ? &deal0_ : &deal1_;
int vuln = vulnerability_[i % 2];
int result = calc_par(ctx, *deal, vuln, &table, &par);
ASSERT_EQ(result, RETURN_NO_FAULT) << "Iteration " << i << " should succeed";
}
SUCCEED();
}