mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
Chess: Gracefully handle ChessEngine disconnections
The GUI now tracks when it becomes disconnected from ChessEngine. If not currently waiting for a move from ChessEngine, it will automatically reconnect on the next engine move. If a disconnection occurs while waiting for a move, the player is asked whether they want to try again or not.
This commit is contained in:
parent
55347ed6a5
commit
8b6c538f2a
Notes:
sideshowbarker
2024-07-16 23:57:20 +09:00
Author: https://github.com/tcl3 Commit: https://github.com/SerenityOS/serenity/commit/8b6c538f2a Pull-request: https://github.com/SerenityOS/serenity/pull/18140 Issue: https://github.com/SerenityOS/serenity/issues/17427 Reviewed-by: https://github.com/LucasChollet Reviewed-by: https://github.com/petelliott
4 changed files with 63 additions and 20 deletions
|
@ -473,12 +473,14 @@ void ChessWidget::input_engine_move()
|
||||||
set_drag_enabled(false);
|
set_drag_enabled(false);
|
||||||
|
|
||||||
set_override_cursor(Gfx::StandardCursor::Wait);
|
set_override_cursor(Gfx::StandardCursor::Wait);
|
||||||
m_engine->get_best_move(board(), 4000, [this, drag_was_enabled](Chess::Move move) {
|
m_engine->get_best_move(board(), 4000, [this, drag_was_enabled](ErrorOr<Chess::Move> move) {
|
||||||
set_override_cursor(Gfx::StandardCursor::None);
|
set_override_cursor(Gfx::StandardCursor::None);
|
||||||
if (!want_engine_move())
|
if (!want_engine_move())
|
||||||
return;
|
return;
|
||||||
set_drag_enabled(drag_was_enabled);
|
set_drag_enabled(drag_was_enabled);
|
||||||
VERIFY(board().apply_move(move));
|
if (!move.is_error())
|
||||||
|
VERIFY(board().apply_move(move.release_value()));
|
||||||
|
|
||||||
m_playback_move_number = m_board.moves().size();
|
m_playback_move_number = m_board.moves().size();
|
||||||
m_playback = false;
|
m_playback = false;
|
||||||
m_board_markings.clear();
|
m_board_markings.clear();
|
||||||
|
|
|
@ -17,6 +17,11 @@ Engine::~Engine()
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine::Engine(StringView command)
|
Engine::Engine(StringView command)
|
||||||
|
: m_command(command)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::connect_to_engine_service()
|
||||||
{
|
{
|
||||||
int wpipefds[2];
|
int wpipefds[2];
|
||||||
int rpipefds[2];
|
int rpipefds[2];
|
||||||
|
@ -35,9 +40,9 @@ Engine::Engine(StringView command)
|
||||||
posix_spawn_file_actions_adddup2(&file_actions, wpipefds[0], STDIN_FILENO);
|
posix_spawn_file_actions_adddup2(&file_actions, wpipefds[0], STDIN_FILENO);
|
||||||
posix_spawn_file_actions_adddup2(&file_actions, rpipefds[1], STDOUT_FILENO);
|
posix_spawn_file_actions_adddup2(&file_actions, rpipefds[1], STDOUT_FILENO);
|
||||||
|
|
||||||
DeprecatedString cstr(command);
|
char const* argv[] = { m_command.characters(), nullptr };
|
||||||
char const* argv[] = { cstr.characters(), nullptr };
|
pid_t pid = -1;
|
||||||
if (posix_spawnp(&m_pid, cstr.characters(), &file_actions, nullptr, const_cast<char**>(argv), environ) < 0) {
|
if (posix_spawnp(&pid, m_command.characters(), &file_actions, nullptr, const_cast<char**>(argv), environ) < 0) {
|
||||||
perror("posix_spawnp");
|
perror("posix_spawnp");
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -56,6 +61,7 @@ Engine::Engine(StringView command)
|
||||||
set_out(outfile);
|
set_out(outfile);
|
||||||
|
|
||||||
send_command(Chess::UCI::UCICommand());
|
send_command(Chess::UCI::UCICommand());
|
||||||
|
m_connected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::handle_bestmove(Chess::UCI::BestMoveCommand const& command)
|
void Engine::handle_bestmove(Chess::UCI::BestMoveCommand const& command)
|
||||||
|
@ -68,5 +74,21 @@ void Engine::handle_bestmove(Chess::UCI::BestMoveCommand const& command)
|
||||||
|
|
||||||
void Engine::quit()
|
void Engine::quit()
|
||||||
{
|
{
|
||||||
|
if (!m_connected)
|
||||||
|
return;
|
||||||
|
|
||||||
send_command(Chess::UCI::QuitCommand());
|
send_command(Chess::UCI::QuitCommand());
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::handle_unexpected_eof()
|
||||||
|
{
|
||||||
|
m_connected = false;
|
||||||
|
if (m_bestmove_callback)
|
||||||
|
m_bestmove_callback(Error::from_errno(EPIPE));
|
||||||
|
|
||||||
|
m_bestmove_callback = nullptr;
|
||||||
|
|
||||||
|
if (on_connection_lost)
|
||||||
|
on_connection_lost();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,17 @@ public:
|
||||||
Engine(Engine const&) = delete;
|
Engine(Engine const&) = delete;
|
||||||
Engine& operator=(Engine const&) = delete;
|
Engine& operator=(Engine const&) = delete;
|
||||||
|
|
||||||
|
Function<void()> on_connection_lost;
|
||||||
|
|
||||||
virtual void handle_bestmove(Chess::UCI::BestMoveCommand const&) override;
|
virtual void handle_bestmove(Chess::UCI::BestMoveCommand const&) override;
|
||||||
|
virtual void handle_unexpected_eof() override;
|
||||||
|
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
void get_best_move(Chess::Board const& board, int time_limit, Callback&& callback)
|
void get_best_move(Chess::Board const& board, int time_limit, Callback&& callback)
|
||||||
{
|
{
|
||||||
|
if (!m_connected)
|
||||||
|
connect_to_engine_service();
|
||||||
|
|
||||||
send_command(Chess::UCI::PositionCommand({}, board.moves()));
|
send_command(Chess::UCI::PositionCommand({}, board.moves()));
|
||||||
Chess::UCI::GoCommand go_command;
|
Chess::UCI::GoCommand go_command;
|
||||||
go_command.movetime = time_limit;
|
go_command.movetime = time_limit;
|
||||||
|
@ -34,6 +40,9 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void quit();
|
void quit();
|
||||||
|
void connect_to_engine_service();
|
||||||
|
|
||||||
Function<void(Chess::Move)> m_bestmove_callback;
|
DeprecatedString m_command;
|
||||||
|
Function<void(ErrorOr<Chess::Move>)> m_bestmove_callback;
|
||||||
|
bool m_connected { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -128,21 +128,31 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
GUI::ActionGroup engines_action_group;
|
GUI::ActionGroup engines_action_group;
|
||||||
engines_action_group.set_exclusive(true);
|
engines_action_group.set_exclusive(true);
|
||||||
auto engine_submenu = TRY(engine_menu->try_add_submenu("&Engine"_short_string));
|
auto engine_submenu = TRY(engine_menu->try_add_submenu("&Engine"_short_string));
|
||||||
for (auto const& engine : { "Human", "ChessEngine" }) {
|
auto human_engine_checkbox = GUI::Action::create_checkable("Human", [&](auto&) {
|
||||||
auto action = GUI::Action::create_checkable(engine, [&](auto& action) {
|
widget->set_engine(nullptr);
|
||||||
if (action.text() == "Human") {
|
});
|
||||||
widget->set_engine(nullptr);
|
human_engine_checkbox->set_checked(true);
|
||||||
} else {
|
engines_action_group.add_action(human_engine_checkbox);
|
||||||
widget->set_engine(Engine::construct(action.text()));
|
TRY(engine_submenu->try_add_action(human_engine_checkbox));
|
||||||
widget->input_engine_move();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
engines_action_group.add_action(*action);
|
|
||||||
if (engine == DeprecatedString("Human"))
|
|
||||||
action->set_checked(true);
|
|
||||||
|
|
||||||
TRY(engine_submenu->try_add_action(*action));
|
auto action = GUI::Action::create_checkable("ChessEngine", [&](auto& action) {
|
||||||
}
|
auto new_engine = Engine::construct(action.text());
|
||||||
|
new_engine->on_connection_lost = [&]() {
|
||||||
|
if (!widget->want_engine_move())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto rc = GUI::MessageBox::show(window, "Connection to the chess engine was lost while waiting for a move. Do you want to try again?"sv, "Chess"sv, GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo);
|
||||||
|
if (rc == GUI::Dialog::ExecResult::Yes)
|
||||||
|
widget->input_engine_move();
|
||||||
|
else
|
||||||
|
human_engine_checkbox->activate();
|
||||||
|
};
|
||||||
|
widget->set_engine(move(new_engine));
|
||||||
|
widget->input_engine_move();
|
||||||
|
});
|
||||||
|
engines_action_group.add_action(*action);
|
||||||
|
|
||||||
|
TRY(engine_submenu->try_add_action(*action));
|
||||||
|
|
||||||
auto help_menu = TRY(window->try_add_menu("&Help"_short_string));
|
auto help_menu = TRY(window->try_add_menu("&Help"_short_string));
|
||||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window)));
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window)));
|
||||||
|
|
Loading…
Reference in a new issue