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_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);
|
||||
if (!want_engine_move())
|
||||
return;
|
||||
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 = false;
|
||||
m_board_markings.clear();
|
||||
|
|
|
@ -17,6 +17,11 @@ Engine::~Engine()
|
|||
}
|
||||
|
||||
Engine::Engine(StringView command)
|
||||
: m_command(command)
|
||||
{
|
||||
}
|
||||
|
||||
void Engine::connect_to_engine_service()
|
||||
{
|
||||
int wpipefds[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, rpipefds[1], STDOUT_FILENO);
|
||||
|
||||
DeprecatedString cstr(command);
|
||||
char const* argv[] = { cstr.characters(), nullptr };
|
||||
if (posix_spawnp(&m_pid, cstr.characters(), &file_actions, nullptr, const_cast<char**>(argv), environ) < 0) {
|
||||
char const* argv[] = { m_command.characters(), nullptr };
|
||||
pid_t pid = -1;
|
||||
if (posix_spawnp(&pid, m_command.characters(), &file_actions, nullptr, const_cast<char**>(argv), environ) < 0) {
|
||||
perror("posix_spawnp");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -56,6 +61,7 @@ Engine::Engine(StringView command)
|
|||
set_out(outfile);
|
||||
|
||||
send_command(Chess::UCI::UCICommand());
|
||||
m_connected = true;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
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& operator=(Engine const&) = delete;
|
||||
|
||||
Function<void()> on_connection_lost;
|
||||
|
||||
virtual void handle_bestmove(Chess::UCI::BestMoveCommand const&) override;
|
||||
virtual void handle_unexpected_eof() override;
|
||||
|
||||
template<typename 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()));
|
||||
Chess::UCI::GoCommand go_command;
|
||||
go_command.movetime = time_limit;
|
||||
|
@ -34,6 +40,9 @@ public:
|
|||
|
||||
private:
|
||||
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;
|
||||
engines_action_group.set_exclusive(true);
|
||||
auto engine_submenu = TRY(engine_menu->try_add_submenu("&Engine"_short_string));
|
||||
for (auto const& engine : { "Human", "ChessEngine" }) {
|
||||
auto action = GUI::Action::create_checkable(engine, [&](auto& action) {
|
||||
if (action.text() == "Human") {
|
||||
widget->set_engine(nullptr);
|
||||
} else {
|
||||
widget->set_engine(Engine::construct(action.text()));
|
||||
widget->input_engine_move();
|
||||
}
|
||||
});
|
||||
engines_action_group.add_action(*action);
|
||||
if (engine == DeprecatedString("Human"))
|
||||
action->set_checked(true);
|
||||
auto human_engine_checkbox = GUI::Action::create_checkable("Human", [&](auto&) {
|
||||
widget->set_engine(nullptr);
|
||||
});
|
||||
human_engine_checkbox->set_checked(true);
|
||||
engines_action_group.add_action(human_engine_checkbox);
|
||||
TRY(engine_submenu->try_add_action(human_engine_checkbox));
|
||||
|
||||
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));
|
||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window)));
|
||||
|
|
Loading…
Reference in a new issue