From cc5c4474c3002d8b7dea6a2ed1935875df1da4d1 Mon Sep 17 00:00:00 2001 From: Calcitem Date: Sun, 24 Dec 2023 22:29:13 +0800 Subject: [PATCH] perfect: Support return value Change-Id: Ic468438dbc337a60fd76b62990d918cea2d0d305 --- src/perfect/perfect_adaptor.cpp | 21 ++++++++++++----- src/perfect/perfect_api.cpp | 10 ++++---- src/perfect/perfect_api.h | 5 ++-- src/perfect/perfect_player.cpp | 23 +++++++++++++++---- src/perfect/perfect_player.h | 8 +++++-- src/perfect/perfect_test.cpp | 12 ++++++---- src/types.h | 5 +++- .../lib/game_page/services/engine/types.dart | 3 ++- .../lib/game_page/widgets/game_header.dart | 13 +++++++---- 9 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/perfect/perfect_adaptor.cpp b/src/perfect/perfect_adaptor.cpp index 15de55e17..186dbd946 100644 --- a/src/perfect/perfect_adaptor.cpp +++ b/src/perfect/perfect_adaptor.cpp @@ -31,21 +31,24 @@ #define USE_DEPRECATED_CLR_API_WITHOUT_WARNING static Move malom_remove_move = MOVE_NONE; +static Value malom_remove_value = VALUE_UNKNOWN; static std::condition_variable cv; int GetBestMove(int whiteBitboard, int blackBitboard, int whiteStonesToPlace, - int blackStonesToPlace, int playerToMove, bool onlyStoneTaking) + int blackStonesToPlace, int playerToMove, bool onlyStoneTaking, + Value &value) { return MalomSolutionAccess::getBestMove(whiteBitboard, blackBitboard, whiteStonesToPlace, blackStonesToPlace, playerToMove, - onlyStoneTaking); + onlyStoneTaking, value); } int perfect_init() { malom_remove_move = MOVE_NONE; + malom_remove_value = VALUE_UNKNOWN; return 0; } @@ -53,6 +56,7 @@ int perfect_init() int perfect_exit() { malom_remove_move = MOVE_NONE; + malom_remove_value = VALUE_UNKNOWN; return 0; } @@ -176,11 +180,15 @@ std::vector convertBitboardMove(int whiteBitboard, int blackBitboard, Value perfect_search(const Position *pos, Move &move) { + Value value = VALUE_UNKNOWN; + if (malom_remove_move != MOVE_NONE) { Move ret = malom_remove_move; + value = malom_remove_value; malom_remove_move = MOVE_NONE; + malom_remove_value = VALUE_UNKNOWN; move = ret; - return VALUE_MOVE_PERFECT; + return value; } std::vector moves; @@ -234,15 +242,17 @@ Value perfect_search(const Position *pos, Move &move) // If this increases the number of stones the player to move has, // then that player will have one less stone to place after the move. // TODO: Do not use -fexceptions + try { int moveBitboard = GetBestMove(whiteBitboard, blackBitboard, whiteStonesToPlace, blackStonesToPlace, - playerToMove, onlyStoneTaking); + playerToMove, onlyStoneTaking, value); moves = convertBitboardMove(whiteBitboard, blackBitboard, playerToMove, moveBitboard); if (moves.size() == 2) { malom_remove_move = moves.at(1); + malom_remove_value = value; } } catch (const std::exception &) { move = MOVE_NONE; @@ -251,8 +261,7 @@ Value perfect_search(const Position *pos, Move &move) move = Move(moves.at(0)); - // TODO: Return the value of the position - return VALUE_MOVE_PERFECT; + return value; } #endif // GABOR_MALOM_PERFECT_AI diff --git a/src/perfect/perfect_api.cpp b/src/perfect/perfect_api.cpp index 947799f93..ae3054d32 100644 --- a/src/perfect/perfect_api.cpp +++ b/src/perfect/perfect_api.cpp @@ -36,7 +36,7 @@ std::exception *MalomSolutionAccess::lastError = nullptr; int MalomSolutionAccess::getBestMove(int whiteBitboard, int blackBitboard, int whiteStonesToPlace, int blackStonesToPlace, int playerToMove, - bool onlyStoneTaking) + bool onlyStoneTaking, Value &value) { initializeIfNeeded(); @@ -95,7 +95,7 @@ int MalomSolutionAccess::getBestMove(int whiteBitboard, int blackBitboard, int ret = 0; try { - ret = pp->chooseRandom(pp->goodMoves(s)).toBitBoard(); + ret = pp->chooseRandom(pp->goodMoves(s, value)).toBitBoard(); } catch (std::out_of_range &) { throw std::runtime_error("We don't have a database entry for this " "position. This can happen either if the " @@ -111,12 +111,14 @@ int MalomSolutionAccess::getBestMove(int whiteBitboard, int blackBitboard, int MalomSolutionAccess::getBestMoveNoException( int whiteBitboard, int blackBitboard, int whiteStonesToPlace, - int blackStonesToPlace, int playerToMove, bool onlyStoneTaking) + int blackStonesToPlace, int playerToMove, bool onlyStoneTaking, + Value &value) { try { lastError = nullptr; return getBestMove(whiteBitboard, blackBitboard, whiteStonesToPlace, - blackStonesToPlace, playerToMove, onlyStoneTaking); + blackStonesToPlace, playerToMove, onlyStoneTaking, + value); } catch (std::exception &e) { lastError = &e; return 0; diff --git a/src/perfect/perfect_api.h b/src/perfect/perfect_api.h index be61eb823..9cb6daa5b 100644 --- a/src/perfect/perfect_api.h +++ b/src/perfect/perfect_api.h @@ -33,12 +33,13 @@ class MalomSolutionAccess public: static int getBestMove(int whiteBitboard, int blackBitboard, int whiteStonesToPlace, int blackStonesToPlace, - int playerToMove, bool onlyStoneTaking); + int playerToMove, bool onlyStoneTaking, + Value &value); static int getBestMoveNoException(int whiteBitboard, int blackBitboard, int whiteStonesToPlace, int blackStonesToPlace, int playerToMove, - bool onlyStoneTaking); + bool onlyStoneTaking, Value &value); static std::string getLastError(); diff --git a/src/perfect/perfect_player.cpp b/src/perfect/perfect_player.cpp index 9006fc236..0d18c61a6 100644 --- a/src/perfect/perfect_player.cpp +++ b/src/perfect/perfect_player.cpp @@ -344,7 +344,7 @@ Wrappers::gui_eval_elem2 PerfectPlayer::moveValue(const GameState &s, template std::vector PerfectPlayer::allMaxBy(std::function f, - const std::vector &l, K minValue) + const std::vector &l, K minValue, Value &value) { std::vector r; @@ -386,20 +386,31 @@ std::vector PerfectPlayer::allMaxBy(std::function f, } } + char e = f(r.at(0)).toString().at(0); + + if (e == 'L') { + value = -VALUE_MATE_PERFECT; + } else if (e == 'W') { + value = VALUE_MATE_PERFECT; + } else { + value = VALUE_DRAW_PERFECT; + } + return r; } #if 1 // Assuming the definition of gui_eval_elem2::min_value function -std::vector PerfectPlayer::goodMoves(const GameState &s) +std::vector PerfectPlayer::goodMoves(const GameState &s, Value &value) { return allMaxBy(std::function( [this, &s](AdvancedMove m) { return moveValue(s, m); }), getMoveList(s), - Wrappers::gui_eval_elem2::min_value(getSec(s))); + Wrappers::gui_eval_elem2::min_value(getSec(s)), value); } #else -std::vector PerfectPlayer::goodMoves(const GameState &s) +std::vector PerfectPlayer::goodMoves(const GameState &s, + Value &value) { auto moveList = getMoveList(s); std::cout << "Move list size: " << moveList.size() @@ -452,8 +463,10 @@ void PerfectPlayer::sendMoveToGUI(AdvancedMove m) void PerfectPlayer::toMove(const GameState &s) { + Value value = VALUE_UNKNOWN; + try { - AdvancedMove mh = chooseRandom(goodMoves(s)); + AdvancedMove mh = chooseRandom(goodMoves(s, value)); sendMoveToGUI(mh); } catch (const std::out_of_range &) { sendMoveToGUI(chooseRandom(getMoveList(s))); diff --git a/src/perfect/perfect_player.h b/src/perfect/perfect_player.h index 6315fd81f..a161499a3 100644 --- a/src/perfect/perfect_player.h +++ b/src/perfect/perfect_player.h @@ -45,6 +45,8 @@ #include #include +#include + enum class CMoveType { SetMove, SlideMove // should be renamed to SlideOrJumpMove @@ -58,6 +60,8 @@ struct AdvancedMove // closure, onlyTaking only includes removal int takeHon; + Value value {VALUE_DRAW_PERFECT}; + int toBitBoard() { if (onlyTaking) { @@ -170,10 +174,10 @@ class PerfectPlayer : public Player template std::vector allMaxBy(std::function f, const std::vector &l, - K minValue); + K minValue, Value &value); // Assuming the definition of gui_eval_elem2::min_value function - std::vector goodMoves(const GameState &s); + std::vector goodMoves(const GameState &s, Value &value); int NGMAfterMove(const GameState &s, AdvancedMove &m); diff --git a/src/perfect/perfect_test.cpp b/src/perfect/perfect_test.cpp index 6ccbf5934..ce2da5c0f 100644 --- a/src/perfect/perfect_test.cpp +++ b/src/perfect/perfect_test.cpp @@ -34,19 +34,23 @@ int perfect_test(int argc, char *argv[]) { + Value value = VALUE_UNKNOWN; + if (argc == 2) { sec_val_path = argv[1]; } // int res = MalomSolutionAccess::getBestMove(0, 0, 9, 9, 0, false); - int res = MalomSolutionAccess::getBestMove(1, 2, 8, 8, 0, false); // Correct + int res = MalomSolutionAccess::getBestMove(1, 2, 8, 8, 0, false, + value); // Correct // output: // 16384 // int res = MalomSolutionAccess::getBestMove(1 + 2 + 4, 8 + 16 + 32, 100, - // 0, 0, false); // tests exception + // 0, 0, false, value); // tests exception // int res = MalomSolutionAccess::getBestMove(1 + 2 + 4, 1 + 8 + 16 + 32, - // 0, 0, 0, false); // tests exception int res = - // MalomSolutionAccess::getBestMove(1 + 2 + 4, 8 + 16 + 32, 0, 0, 0, true); + // 0, 0, 0, false, value); // tests exception int res = + // MalomSolutionAccess::getBestMove(1 + 2 + 4, 8 + 16 + 32, 0, 0, 0, true, + // value); // // Correct output: any of 8, 16, 32 printf("GetBestMove result: %d\n", res); diff --git a/src/types.h b/src/types.h index 3163228e8..5d16d8249 100644 --- a/src/types.h +++ b/src/types.h @@ -174,11 +174,14 @@ enum Bound : uint8_t { enum Value : int8_t { VALUE_ZERO = 0, VALUE_DRAW = 0, + VALUE_DRAW_PERFECT = 1, + #ifdef ENDGAME_LEARNING VALUE_KNOWN_WIN = 25, #endif - VALUE_MOVE_PERFECT = 1, + VALUE_MATE = 80, + VALUE_MATE_PERFECT = 79, VALUE_UNIQUE = 100, VALUE_INFINITE = 125, VALUE_UNKNOWN = INT8_MIN, diff --git a/src/ui/flutter_app/lib/game_page/services/engine/types.dart b/src/ui/flutter_app/lib/game_page/services/engine/types.dart index 5ff42d4f8..0027c36da 100644 --- a/src/ui/flutter_app/lib/game_page/services/engine/types.dart +++ b/src/ui/flutter_app/lib/game_page/services/engine/types.dart @@ -332,7 +332,8 @@ extension GameResultExtension on GameResult { } } -const int valueMovePerfect = 1; +const int valueDrawPerfect = 1; +const int valueMatePerfect = 79; const int valueUnique = 100; const int valueEachPiece = 5; diff --git a/src/ui/flutter_app/lib/game_page/widgets/game_header.dart b/src/ui/flutter_app/lib/game_page/widgets/game_header.dart index a5fbe230c..662c8b939 100644 --- a/src/ui/flutter_app/lib/game_page/widgets/game_header.dart +++ b/src/ui/flutter_app/lib/game_page/widgets/game_header.dart @@ -75,8 +75,11 @@ class _GameHeaderState extends State { int value = GameController().value == null ? 0 : int.parse(GameController().value!); - if (abs(value) != valueMovePerfect && - DB().displaySettings.isPositionalAdvantageIndicatorShown) { + final bool perfect = abs(value) == valueDrawPerfect || + abs(value) == valueMatePerfect; + final double opacity = perfect? 0.5 : 1; + + if (DB().displaySettings.isPositionalAdvantageIndicatorShown) { const int valueLimit = 100; // TODO: Modify engine to return suitable value @@ -111,12 +114,12 @@ class _GameHeaderState extends State { Container( height: 2, width: dividerWhiteLength.toDouble(), - color: DB().colorSettings.whitePieceColor, + color: DB().colorSettings.whitePieceColor.withOpacity(opacity), ), Container( height: 2, width: dividerBlackLength.toDouble(), - color: DB().colorSettings.blackPieceColor, + color: DB().colorSettings.blackPieceColor.withOpacity(opacity), ), ], ), @@ -127,7 +130,7 @@ class _GameHeaderState extends State { width: 180, margin: const EdgeInsets.only(bottom: AppTheme.boardMargin), decoration: BoxDecoration( - color: DB().colorSettings.boardBackgroundColor, + color: DB().colorSettings.boardBackgroundColor.withOpacity(opacity), borderRadius: BorderRadius.circular(2), ), );