#include <ab_search.hpp>
#include <dump.hpp>
#include <init.hpp>
#include "solver_if.hpp"
#include <api/solve_board.hpp>
#include <lookup_tables/lookup_tables.hpp>
#include <solver_context/solver_context.hpp>
#include <system/scheduler.hpp>
#include <system/system.hpp>
#include <system/timer_list.hpp>
#include <trans_table/trans_table.hpp>
extern System sysdep;
extern Memory memory;
extern Scheduler scheduler;
auto board_range_checks(
const Deal& dl,
const int target,
const int solutions,
const int mode) -> int;
auto board_value_checks(
SolverContext& ctx,
const Deal& dl,
const int target,
const int solutions,
const int mode) -> int;
auto last_trick_winner(
const Deal& dl,
const std::shared_ptr<ThreadData>& thrp,
const int handToPlay,
const int hand_rel_first,
int& leadRank,
int& leadSuit,
int& leadSideWins) -> void;
bool (* AB_ptr_list[DDS_HANDS])(
Pos * posPoint,
const int target,
const int depth,
SolverContext& ctx)
= { ab_search, ab_search_1, ab_search_2, ab_search_3 };
bool (* AB_ptr_trace_list[DDS_HANDS])(
Pos * posPoint,
const int target,
const int depth,
SolverContext& ctx)
= { ab_search_0, ab_search_1, ab_search_2, ab_search_3 };
void (* Make_ptr_list[3])(
Pos * posPoint,
const int depth,
MoveType const * mply)
= { make_0, make_1, make_2 };
int STDCALL SolveBoard(
Deal dl,
int target,
int solutions,
int mode,
FutureTricks * futp,
int thrId)
{
if (! sysdep.thread_ok(thrId))
return RETURN_THREAD_INDEX;
SolverContext outer_ctx;
return solve_board(outer_ctx, dl, target, solutions, mode, futp);
}
auto solve_board_internal(
SolverContext& ctx,
const Deal& dl,
const int target,
const int solutions,
const int mode,
FutureTricks * futp) -> int
{
int ret = board_range_checks(dl, target, solutions, mode);
if (ret != RETURN_NO_FAULT)
return ret;
auto thrp = ctx.thread();
bool newDeal = false;
bool newTrump = false;
unsigned diffDeal = 0;
unsigned aggDeal = 0;
bool similarDeal;
int cardCount = 0;
int ind, forb, noMoves;
for (int h = 0; h < DDS_HANDS; h++)
{
for (int s = 0; s < DDS_SUITS; s++)
{
unsigned int c = dl.remainCards[h][s] >> 2;
cardCount += count_table[c];
diffDeal += (c ^ (thrp->suit[h][s]));
aggDeal += c;
if (thrp->suit[h][s] != c)
{
thrp->suit[h][s] = static_cast<unsigned short>(c);
newDeal = true;
}
}
}
if (newDeal)
{
if (diffDeal == 0)
similarDeal = true;
else if ((aggDeal / diffDeal) > SIMILARDEALLIMIT)
similarDeal = true;
else
similarDeal = false;
}
else
similarDeal = false;
if (dl.trump != thrp->trump)
newTrump = true;
thrp->trump = dl.trump;
ctx.search().ini_depth() = cardCount - 4;
int ini_depth = ctx.search().ini_depth();
int trick = (ini_depth + 3) >> 2;
int hand_rel_first = (48 - ini_depth) % 4;
int handToPlay = HAND_ID(dl.first, hand_rel_first);
ctx.search().trick_nodes() = 0;
thrp->lookAheadPos.hand_rel_first = hand_rel_first;
thrp->lookAheadPos.first[ini_depth] = dl.first;
thrp->lookAheadPos.tricks_max = 0;
MoveType mv = {0, 0, 0, 0};
ctx.search().clear_forbidden_moves();
ret = board_value_checks(ctx, dl, target, solutions, mode);
if (ret != RETURN_NO_FAULT)
return ret;
if (cardCount <= 4)
{
int leadRank, leadSuit, leadSideWins;
last_trick_winner(dl, thrp, handToPlay, hand_rel_first,
leadRank, leadSuit, leadSideWins);
futp->nodes = 0;
futp->cards = 1;
futp->suit[0] = leadSuit;
futp->rank[0] = leadRank;
futp->equals[0] = 0;
futp->score[0] = (target == 0 && solutions < 3 ? 0 : leadSideWins);
goto SOLVER_DONE;
}
{
if ((mode != 2) &&
(((newDeal) && (! similarDeal)) ||
newTrump ||
(ctx.search().nodes() > SIMILARMAXWINNODES)))
{
ResetReason reason = ResetReason::Unknown;
if (ctx.search().nodes() > SIMILARMAXWINNODES)
reason = ResetReason::TooManyNodes;
else if (newDeal && ! similarDeal)
reason = ResetReason::NewDeal;
else if (newTrump)
reason = ResetReason::NewTrump;
ctx.trans_table()->reset_memory(reason);
}
}
if (newDeal)
{
SetDeal(thrp);
SetDealTables(ctx);
}
else if (ctx.search().analysis_flag())
{
SetDeal(thrp);
}
ctx.search().analysis_flag() = false;
if (handToPlay == 0 || handToPlay == 2)
{
ctx.search().node_type_store(0) = MAXNODE;
ctx.search().node_type_store(1) = MINNODE;
ctx.search().node_type_store(2) = MAXNODE;
ctx.search().node_type_store(3) = MINNODE;
}
else
{
ctx.search().node_type_store(0) = MINNODE;
ctx.search().node_type_store(1) = MAXNODE;
ctx.search().node_type_store(2) = MINNODE;
ctx.search().node_type_store(3) = MAXNODE;
}
for (int k = 0; k < hand_rel_first; k++)
{
mv.rank = dl.currentTrickRank[k];
mv.suit = dl.currentTrickSuit[k];
mv.sequence = 0;
ctx.move_gen().init(
trick,
k,
dl.currentTrickRank,
dl.currentTrickSuit,
thrp->lookAheadPos.rank_in_suit,
thrp->trump,
thrp->lookAheadPos.first[ini_depth]);
if (k == 0)
{
ctx.move_gen().move_gen_0(
trick,
thrp->lookAheadPos,
ctx.search().best_move(ini_depth),
ctx.search().best_move_tt(ini_depth),
thrp->rel);
}
else
ctx.move_gen().move_gen_123(
trick,
k,
thrp->lookAheadPos);
thrp->lookAheadPos.move[ini_depth + hand_rel_first - k] = mv;
ctx.move_gen().make_specific(mv, trick, k);
}
InitWinners(dl, thrp->lookAheadPos, thrp);
#ifdef DDS_AB_STATS
thrp->ABStats.Reset();
thrp->ABStats.ResetCum();
#endif
#ifdef DDS_TOP_LEVEL
{
ctx.search().nodes() = 0;
}
#endif
ctx.move_gen().init(
trick,
hand_rel_first,
dl.currentTrickRank,
dl.currentTrickSuit,
thrp->lookAheadPos.rank_in_suit,
thrp->trump,
thrp->lookAheadPos.first[ini_depth]);
if (hand_rel_first == 0)
{
ctx.move_gen().move_gen_0(
trick,
thrp->lookAheadPos,
ctx.search().best_move(ini_depth),
ctx.search().best_move_tt(ini_depth),
thrp->rel);
}
else
ctx.move_gen().move_gen_123(
trick,
hand_rel_first,
thrp->lookAheadPos);
noMoves = ctx.move_gen().get_length(trick, hand_rel_first);
if (mode == 0 && noMoves == 1 && solutions != 3)
{
MoveType const * mp = ctx.move_gen().make_next_simple(trick, hand_rel_first);
futp->nodes = 0;
futp->cards = 1;
futp->suit[0] = mp->suit;
futp->rank[0] = mp->rank;
futp->equals[0] = mp->sequence << 2;
futp->score[0] = -2;
goto SOLVER_DONE;
}
if (solutions == 3)
{
int guess = 7 - (handToPlay & 0x1);
int upperbound = 13;
int lowerbound = 0;
futp->cards = noMoves;
for (int mno = 0; mno < noMoves; mno++)
{
do
{
ctx.reset_best_moves_lite();
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = (* AB_ptr_list[hand_rel_first])(
&thrp->lookAheadPos,
guess,
ini_depth,
ctx);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, guess, lowerbound, upperbound, 1);
#endif
if (thrp->val)
{
mv = ctx.search().best_move(ini_depth);
lowerbound = guess++;
}
else
upperbound = --guess;
}
while (lowerbound < upperbound);
if (lowerbound)
{
ctx.search().best_move(ini_depth) = mv;
futp->suit[mno] = mv.suit;
futp->rank[mno] = mv.rank;
futp->equals[mno] = mv.sequence << 2;
futp->score[mno] = lowerbound;
ctx.search().forbidden_move(mno + 1).suit = mv.suit;
ctx.search().forbidden_move(mno + 1).rank = mv.rank;
guess = lowerbound;
lowerbound = 0;
}
else
{
int noLeft = ctx.move_gen().get_length(trick, hand_rel_first);
ctx.move_gen().rewind(trick, hand_rel_first);
for (int j = 0; j < noLeft; j++)
{
MoveType const * mp =
ctx.move_gen().make_next_simple(trick, hand_rel_first);
futp->suit[mno + j] = mp->suit;
futp->rank[mno + j] = mp->rank;
futp->equals[mno + j] = mp->sequence << 2;
futp->score[mno + j] = 0;
}
break;
}
}
goto SOLVER_STATS;
}
else if (target == 0)
{
futp->nodes = 0;
futp->cards = (solutions == 1 ? 1 : noMoves);
for (int mno = 0; mno < noMoves; mno++)
{
MoveType const * mp =
ctx.move_gen().make_next_simple(trick, hand_rel_first);
futp->suit[mno] = mp->suit;
futp->rank[mno] = mp->rank;
futp->equals[mno] = mp->sequence << 2;
futp->score[mno] = 0;
}
goto SOLVER_DONE;
}
else if (target == -1)
{
int guess = 7 - (handToPlay & 0x1);
int upperbound = 13;
int lowerbound = 0;
do
{
ctx.reset_best_moves_lite();
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = (* AB_ptr_list[hand_rel_first])(&thrp->lookAheadPos,
guess,
ini_depth,
ctx);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, guess, lowerbound, upperbound, 1);
#endif
if (thrp->val)
{
mv = ctx.search().best_move(ini_depth);
lowerbound = guess++;
}
else
upperbound = --guess;
}
while (lowerbound < upperbound);
ctx.search().best_move(ini_depth) = mv;
if (lowerbound == 0)
{
if (solutions == 1) futp->cards = 1;
else futp->cards = noMoves;
ctx.move_gen().rewind(trick, hand_rel_first);
for (int i = 0; i < noMoves; i++)
{
MoveType const * mp =
ctx.move_gen().make_next_simple(trick, hand_rel_first);
futp->score[i] = 0;
futp->suit[i] = mp->suit;
futp->rank[i] = mp->rank;
futp->equals[i] = mp->sequence << 2;
}
goto SOLVER_STATS;
}
else {
futp->cards = 1;
futp->score[0] = lowerbound;
futp->suit[0] = mv.suit;
futp->rank[0] = mv.rank;
futp->equals[0] = mv.sequence << 2;
if (solutions != 2)
goto SOLVER_STATS;
}
}
else
{
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = (* AB_ptr_list[hand_rel_first])(
&thrp->lookAheadPos,
target,
ini_depth,
ctx);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, target, -1, -1, 0);
#endif
if (! thrp->val)
{
futp->cards = 0;
futp->score[0] = (target > 1 ? -1 : 0);
goto SOLVER_STATS;
}
else
{
futp->cards = 1;
futp->suit[0] = ctx.search().best_move(ini_depth).suit;
futp->rank[0] = ctx.search().best_move(ini_depth).rank;
futp->equals[0] = ctx.search().best_move(ini_depth).sequence << 2;
futp->score[0] = target;
if (solutions != 2)
goto SOLVER_STATS;
}
}
forb = 1;
ind = 1;
while (ind < noMoves)
{
ctx.move_gen().rewind(trick, hand_rel_first);
int num = ctx.move_gen().get_length(trick, hand_rel_first);
for (int k = 0; k < num; k++)
{
MoveType const * mp =
ctx.move_gen().make_next_simple(trick, hand_rel_first);
ctx.search().forbidden_move(forb) = * mp;
forb++;
if ((ctx.search().best_move(ini_depth).suit == mp->suit) &&
(ctx.search().best_move(ini_depth).rank == mp->rank))
break;
}
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = (* AB_ptr_list[hand_rel_first])(
&thrp->lookAheadPos,
futp->score[0],
ini_depth,
ctx);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, target, -1, -1, 2);
#endif
if (! thrp->val)
break;
futp->cards = ind + 1;
futp->suit[ind] = ctx.search().best_move(ini_depth).suit;
futp->rank[ind] = ctx.search().best_move(ini_depth).rank;
futp->equals[ind] = ctx.search().best_move(ini_depth).sequence << 2;
futp->score[ind] = futp->score[0];
ind++;
}
SOLVER_STATS:
{
ctx.search().clear_forbidden_moves();
}
#ifdef DDS_TIMING
thrp->timerList.PrintStats(thrp->fileTimerList.GetStream());
#endif
#ifdef DDS_TT_STATS
{
ctx.trans_table()->print_summary_suit_stats(thrp->fileTTstats.GetStream());
ctx.trans_table()->print_summary_entry_stats(thrp->fileTTstats.GetStream());
}
{
ctx.trans_table()->print_node_stats(thrp->fileTTstats.GetStream());
ctx.trans_table()->print_reset_stats(thrp->fileTTstats.GetStream());
}
#endif
#ifdef DDS_MOVES
ctx.move_gen().print_trick_stats(thrp->fileMoves.GetStream());
#ifdef DDS_MOVES_DETAILS
ctx.move_gen().print_trick_details(thrp->fileMoves.GetStream());
#endif
ctx.move_gen().print_function_stats(thrp->fileMoves.GetStream());
#endif
SOLVER_DONE:
{
thrp->memUsed = ctx.trans_table()->memory_in_use() + ThreadMemoryUsed();
}
{
futp->nodes = ctx.search().trick_nodes();
}
#ifdef DDS_MEMORY_LEAKS_WIN32
_CrtDumpMemoryLeaks();
#endif
return RETURN_NO_FAULT;
}
auto solve_same_board(
SolverContext& ctx,
const Deal& dl,
FutureTricks * futp,
const int hint) -> int
{
auto thrp = ctx.thread();
int ini_depth = ctx.search().ini_depth();
int trick = (ini_depth + 3) >> 2;
{
ctx.search().trick_nodes() = 0;
}
thrp->lookAheadPos.first[ini_depth] = dl.first;
{
if (dl.first == 0 || dl.first == 2)
{
ctx.search().node_type_store(0) = MAXNODE;
ctx.search().node_type_store(1) = MINNODE;
ctx.search().node_type_store(2) = MAXNODE;
ctx.search().node_type_store(3) = MINNODE;
}
else
{
ctx.search().node_type_store(0) = MINNODE;
ctx.search().node_type_store(1) = MAXNODE;
ctx.search().node_type_store(2) = MINNODE;
ctx.search().node_type_store(3) = MAXNODE;
}
}
#ifdef DDS_AB_STATS
thrp->ABStats.Reset();
thrp->ABStats.ResetCum();
#endif
#ifdef DDS_TOP_LEVEL
{
ctx.search().nodes() = 0;
}
#endif
ctx.move_gen().reinit(trick, dl.first);
int guess = hint;
int lowerbound = 0;
int upperbound = 13;
do
{
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = ab_search(
&thrp->lookAheadPos,
guess,
ini_depth,
ctx);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, guess, lowerbound, upperbound, 1);
#endif
if (thrp->val)
lowerbound = guess++;
else
upperbound = --guess;
}
while (lowerbound < upperbound);
futp->cards = 1;
futp->score[0] = lowerbound;
thrp->memUsed = ctx.trans_table()->memory_in_use() +
ThreadMemoryUsed();
#ifdef DDS_TIMING
thrp->timerList.PrintStats(thrp->fileTimerList.GetStream());
#endif
#ifdef DDS_TT_STATS
{
ctx.trans_table()->print_summary_suit_stats(thrp->fileTTstats.GetStream());
ctx.trans_table()->print_summary_entry_stats(thrp->fileTTstats.GetStream());
}
{
ctx.trans_table()->print_node_stats(thrp->fileTTstats.GetStream());
ctx.trans_table()->print_reset_stats(thrp->fileTTstats.GetStream());
}
#endif
#ifdef DDS_MOVES
ctx.move_gen().print_trick_stats(thrp->fileMoves.GetStream());
#ifdef DDS_MOVES_DETAILS
ctx.move_gen().print_trick_details(thrp->fileMoves.GetStream());
#endif
ctx.move_gen().print_function_stats(thrp->fileMoves.GetStream());
#endif
{
futp->nodes = ctx.search().trick_nodes();
}
#ifdef DDS_MEMORY_LEAKS_WIN32
_CrtDumpMemoryLeaks();
#endif
return RETURN_NO_FAULT;
}
auto analyse_later_board(
const std::shared_ptr<ThreadData>& thrp,
const int leadHand,
MoveType const * move,
const int hint,
const int hintDir,
FutureTricks * futp) -> int
{
SolverContext ctxLater{thrp};
int ini_depth = --ctxLater.search().ini_depth();
int cardCount = ini_depth + 4;
int trick = (ini_depth + 3) >> 2;
int hand_rel_first = (48 - ini_depth) % 4;
{
ctxLater.search().trick_nodes() = 0;
}
{
ctxLater.search().analysis_flag() = true;
}
int handToPlay = HAND_ID(leadHand, hand_rel_first);
{
if (handToPlay == 0 || handToPlay == 2)
{
ctxLater.search().node_type_store(0) = MAXNODE;
ctxLater.search().node_type_store(1) = MINNODE;
ctxLater.search().node_type_store(2) = MAXNODE;
ctxLater.search().node_type_store(3) = MINNODE;
}
else
{
ctxLater.search().node_type_store(0) = MINNODE;
ctxLater.search().node_type_store(1) = MAXNODE;
ctxLater.search().node_type_store(2) = MINNODE;
ctxLater.search().node_type_store(3) = MAXNODE;
}
}
if (hand_rel_first == 0)
{
ctxLater.move_gen().make_specific(* move, trick + 1, 3);
unsigned short int ourWinRanks[DDS_SUITS]; make_3(&thrp->lookAheadPos, ourWinRanks, ini_depth + 1, move, ctxLater);
}
else if (hand_rel_first == 1)
{
ctxLater.move_gen().make_specific(* move, trick, 0);
make_0(&thrp->lookAheadPos, ini_depth + 1, move);
}
else if (hand_rel_first == 2)
{
ctxLater.move_gen().make_specific(* move, trick, 1);
make_1(&thrp->lookAheadPos, ini_depth + 1, move);
}
else
{
ctxLater.move_gen().make_specific(* move, trick, 2);
make_2(&thrp->lookAheadPos, ini_depth + 1, move);
}
if (cardCount <= 4)
{
EvalType eval = evaluate_with_context(&thrp->lookAheadPos, thrp->trump, ctxLater);
futp->score[0] = eval.tricks;
futp->nodes = 0;
return RETURN_NO_FAULT;
}
#ifdef DDS_AB_STATS
thrp->ABStats.Reset();
thrp->ABStats.ResetCum();
#endif
#ifdef DDS_TOP_LEVEL
{
ctxLater.search().nodes() = 0;
}
#endif
int guess = hint,
lowerbound,
upperbound;
if (hintDir == 0)
{
lowerbound = hint;
upperbound = 13;
}
else
{
lowerbound = 0;
upperbound = hint;
}
do
{
ctxLater.reset_best_moves_lite();
TIMER_START(TIMER_NO_AB, ini_depth);
thrp->val = (* AB_ptr_trace_list[hand_rel_first])(
&thrp->lookAheadPos,
guess,
ini_depth,
ctxLater);
TIMER_END(TIMER_NO_AB, ini_depth);
#ifdef DDS_TOP_LEVEL
DumpTopLevel(thrp->fileTopLevel.GetStream(),
thrp, guess, lowerbound, upperbound, 1);
#endif
if (thrp->val)
lowerbound = guess++;
else
upperbound = --guess;
}
while (lowerbound < upperbound);
futp->score[0] = lowerbound;
{
futp->nodes = ctxLater.search().trick_nodes();
}
thrp->memUsed = ctxLater.trans_table()->memory_in_use() +
ThreadMemoryUsed();
#ifdef DDS_TIMING
thrp->timerList.PrintStats(thrp->fileTimerList.GetStream());
#endif
#ifdef DDS_TT_STATS
{
ctxLater.trans_table()->print_summary_suit_stats(thrp->fileTTstats.GetStream());
ctxLater.trans_table()->print_summary_entry_stats(thrp->fileTTstats.GetStream());
}
{
ctxLater.trans_table()->print_node_stats(thrp->fileTTstats.GetStream());
ctxLater.trans_table()->print_reset_stats(thrp->fileTTstats.GetStream());
}
#endif
#ifdef DDS_MOVES
ctxLater.move_gen().print_trick_stats(thrp->fileMoves.GetStream());
#ifdef DDS_MOVES_DETAILS
ctxLater.move_gen().print_trick_details(thrp->fileMoves.GetStream());
#endif
ctxLater.move_gen().print_function_stats(thrp->fileMoves.GetStream());
#endif
#ifdef DDS_MEMORY_LEAKS_WIN32
_CrtDumpMemoryLeaks();
#endif
return RETURN_NO_FAULT;
}
auto board_range_checks(
const Deal& dl,
const int target,
const int solutions,
const int mode) -> int
{
if (target < -1)
{
DumpInput(RETURN_TARGET_WRONG_LO, dl, target, solutions, mode);
return RETURN_TARGET_WRONG_LO;
}
if (target > 13)
{
DumpInput(RETURN_TARGET_WRONG_HI, dl, target, solutions, mode);
return RETURN_TARGET_WRONG_HI;
}
if (solutions < 1)
{
DumpInput(RETURN_SOLNS_WRONG_LO, dl, target, solutions, mode);
return RETURN_SOLNS_WRONG_LO;
}
if (solutions > 3)
{
DumpInput(RETURN_SOLNS_WRONG_HI, dl, target, solutions, mode);
return RETURN_SOLNS_WRONG_HI;
}
if (mode < 0)
{
DumpInput(RETURN_MODE_WRONG_LO, dl, target, solutions, mode);
return RETURN_MODE_WRONG_LO;
}
if (mode > 2)
{
DumpInput(RETURN_MODE_WRONG_HI, dl, target, solutions, mode);
return RETURN_MODE_WRONG_HI;
}
if (dl.trump < 0 || dl.trump > 4)
{
DumpInput(RETURN_TRUMP_WRONG, dl, target, solutions, mode);
return RETURN_TRUMP_WRONG;
}
if (dl.first < 0 || dl.first > 3)
{
DumpInput(RETURN_FIRST_WRONG, dl, target, solutions, mode);
return RETURN_FIRST_WRONG;
}
int rankSeen[3] = {0, 0, 0};
for (int k = 0; k < 3; k++)
{
int r = dl.currentTrickRank[k];
if (r == 0)
continue;
rankSeen[k] = 1;
if (r < 2 || r > 14)
{
DumpInput(RETURN_SUIT_OR_RANK, dl, target, solutions, mode);
return RETURN_SUIT_OR_RANK;
}
if (dl.currentTrickSuit[k] < 0 || dl.currentTrickSuit[k] > 3)
{
DumpInput(RETURN_SUIT_OR_RANK, dl, target, solutions, mode);
return RETURN_SUIT_OR_RANK;
}
}
if ((rankSeen[2] && (! rankSeen[1] || ! rankSeen[0])) ||
(rankSeen[1] && ! rankSeen[0]))
{
DumpInput(RETURN_SUIT_OR_RANK, dl, target, solutions, mode);
return RETURN_SUIT_OR_RANK;
}
for (int h = 0; h < DDS_HANDS; h++)
{
for (int s = 0; s < DDS_SUITS; s++)
{
unsigned c = dl.remainCards[h][s];
if (c != 0 && (c < 0x0004 || c >= 0x8000))
{
DumpInput(RETURN_SUIT_OR_RANK, dl, target, solutions, mode);
return RETURN_SUIT_OR_RANK;
}
}
}
return RETURN_NO_FAULT;
}
auto board_value_checks(
SolverContext& ctx,
const Deal& dl,
const int target,
const int solutions,
const int mode) -> int
{
auto thrp = ctx.thread();
int cardCount = ctx.search().ini_depth() + 4;
if (cardCount <= 0)
{
DumpInput(RETURN_ZERO_CARDS, dl, target, solutions, mode);
return RETURN_ZERO_CARDS;
}
if (cardCount > 52)
{
DumpInput(RETURN_TOO_MANY_CARDS, dl, target, solutions, mode);
return RETURN_TOO_MANY_CARDS;
}
int totalTricks;
if (cardCount % 4)
totalTricks = ((cardCount - 4) >> 2) + 2;
else
totalTricks = ((cardCount - 4) >> 2) + 1;
if (totalTricks < target)
{
DumpInput(RETURN_TARGET_TOO_HIGH, dl, target, solutions, mode);
return RETURN_TARGET_TOO_HIGH;
}
int hand_rel_first = thrp->lookAheadPos.hand_rel_first;
int noOfCardsPerHand[DDS_HANDS] = {0, 0, 0, 0};
for (int k = 0; k < hand_rel_first; k++)
noOfCardsPerHand[HAND_ID(dl.first, k)] = 1;
for (int h = 0; h < DDS_HANDS; h++)
for (int s = 0; s < DDS_SUITS; s++)
noOfCardsPerHand[h] += count_table[thrp->suit[h][s]];
for (int h = 1; h < DDS_HANDS; h++)
{
if (noOfCardsPerHand[h] != noOfCardsPerHand[0])
{
DumpInput(RETURN_CARD_COUNT, dl, target, solutions, mode);
return RETURN_CARD_COUNT;
}
}
for (int k = 0; k < hand_rel_first; k++)
{
unsigned short int aggrRemain = 0;
for (int h = 0; h < DDS_HANDS; h++)
aggrRemain |= (dl.remainCards[h][dl.currentTrickSuit[k]] >> 2);
if ((aggrRemain & bit_map_rank[dl.currentTrickRank[k]]) != 0)
{
DumpInput(RETURN_PLAYED_CARD, dl, target, solutions, mode);
return RETURN_PLAYED_CARD;
}
}
for (int s = 0; s < DDS_SUITS; s++)
{
for (int r = 2; r <= 14; r++)
{
bool found = false;
for (int h = 0; h < DDS_HANDS; h++)
{
if ((thrp->suit[h][s] & bit_map_rank[r]) != 0)
{
if (found)
{
DumpInput(RETURN_DUPLICATE_CARDS, dl,
target, solutions, mode);
return RETURN_DUPLICATE_CARDS;
}
else
found = true;
}
}
}
}
return RETURN_NO_FAULT;
}
auto last_trick_winner(
const Deal& dl,
const std::shared_ptr<ThreadData>& thrp,
const int handToPlay,
const int hand_rel_first,
int& leadRank,
int& leadSuit,
int& leadSideWins) -> void
{
int lastTrickSuit[DDS_HANDS],
lastTrickRank[DDS_HANDS],
h,
hp;
for (h = 0; h < hand_rel_first; h++)
{
hp = HAND_ID(dl.first, h);
lastTrickSuit[hp] = dl.currentTrickSuit[h];
lastTrickRank[hp] = dl.currentTrickRank[h];
}
for (h = hand_rel_first; h < DDS_HANDS; h++)
{
hp = HAND_ID(dl.first, h);
for (int s = 0; s < DDS_SUITS; s++)
{
if (thrp->suit[hp][s] != 0)
{
lastTrickSuit[hp] = s;
lastTrickRank[hp] = highest_rank[thrp->suit[hp][s]];
break;
}
}
}
int maxRank = 0,
maxSuit,
maxHand = -1;
if (dl.trump != DDS_NOTRUMP)
{
for (h = 0; h < DDS_HANDS; h++)
{
if ((lastTrickSuit[h] == dl.trump) &&
(lastTrickRank[h] > maxRank))
{
maxRank = lastTrickRank[h];
maxSuit = dl.trump;
maxHand = h;
}
}
}
if (maxRank == 0)
{
maxRank = lastTrickRank[dl.first];
maxSuit = lastTrickSuit[dl.first];
maxHand = dl.first;
for (h = 0; h < DDS_HANDS; h++)
{
if (lastTrickSuit[h] == maxSuit &&
lastTrickRank[h] > maxRank)
{
maxHand = h;
maxRank = lastTrickRank[h];
}
}
}
hp = HAND_ID(dl.first, hand_rel_first);
leadRank = lastTrickRank[hp];
leadSuit = lastTrickSuit[hp];
leadSideWins = ((handToPlay == maxHand ||
partner[handToPlay] == maxHand) ? 1 : 0);
}