lib94/bench/bench_window.cpp
2023-05-30 22:06:09 -04:00

352 lines
11 KiB
C++

#include <fstream>
#include "bench_window.hpp"
#include "main.hpp"
bench_window::bench_window()
: runner_stop_now(false),
runner_stop_on_death(true),
runner_stop_on_win(true),
runner_hyperspeed(false),
core_fading(true),
control_box(Gtk::Orientation::VERTICAL),
new_round_button("new round"),
single_step_button("single step"),
start_button("start"),
stop_button("stop"),
core_fading_toggle("core fading"),
hyperspeed_toggle("hyperspeed"),
pause_on_death_toggle("pause on death"),
pause_on_win_toggle("pause on win"),
target_core_rate_label(),
target_core_rate_scale(),
add_warrior_button("add warrior"),
remove_warrior_button("remove warrior"),
runner({}),
runner_active(false) {
warrior_list_store = Gtk::TreeStore::create(warrior_list_columns);
instructions_store = Gtk::TreeStore::create(instructions_columns);
warrior_list_view.set_model(warrior_list_store);
instructions_view.set_model(instructions_store);
instructions_view.get_selection()->set_mode(Gtk::SelectionMode::NONE);
warrior_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &bench_window::update_buttons));
warrior_list_view.append_column("name", warrior_list_columns.warrior_name);
warrior_list_view.append_column("#p", warrior_list_columns.processes);
warrior_list_view.append_column("pc", warrior_list_columns.next_pc);
instructions_view.append_column("", instructions_columns.address);
instructions_view.append_column("", instructions_columns.instruction);
for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i) {
auto row = instructions_store->append();
(*row)[instructions_columns.address] = i;
}
update_ui();
core_fading_toggle.activate();
pause_on_death_toggle.activate();
pause_on_win_toggle.activate();
new_round_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_new_round));
single_step_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_single_step));
start_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_start));
stop_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_stop));
add_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_add_warrior));
remove_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_remove_warrior));
core_fading_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_core_fading));
hyperspeed_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_hyperspeed));
pause_on_death_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_death));
pause_on_win_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_win));
target_core_rate_scale.signal_value_changed().connect(sigc::mem_fun(*this, &bench_window::on_target_core_rate_changed));
target_core_rate_scale.set_range(0.0, 4.0);
target_core_rate_scale.set_value(std::log10(20.0));
control_box.append(new_round_button);
control_box.append(single_step_button);
control_box.append(start_button);
control_box.append(stop_button);
control_box.append(core_fading_toggle);
control_box.append(hyperspeed_toggle);
control_box.append(pause_on_death_toggle);
control_box.append(pause_on_win_toggle);
control_box.append(target_core_rate_label);
control_box.append(target_core_rate_scale);
control_box.append(add_warrior_button);
control_box.append(remove_warrior_button);
warrior_list_scroll.set_child(warrior_list_view);
warrior_list_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
warrior_list_scroll.set_vexpand();
control_box.append(warrior_list_scroll);
control_box.append(core_rate_label);
control_box.append(core_render_label);
instructions_scroll.set_child(instructions_view);
instructions_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
control_box.set_size_request(200, -1);
core.set_expand();
instructions_scroll.set_size_request(200, -1);
main_box.append(control_box);
main_box.append(core);
main_box.append(instructions_scroll);
set_child(main_box);
set_title("lib94 bench");
runner_update_ui_dispatcher.connect(sigc::mem_fun(*this, &bench_window::update_ui));
runner_stopping_dispatcher.connect(sigc::mem_fun(*this, &bench_window::on_runner_stopping));
}
void bench_window::add_modified_for_instruction_view(std::set<lib94::number_t> the_set) {
for (lib94::number_t n : the_set)
modified_addresses_for_instructions_view.insert(n);
}
const lib94::warrior *bench_window::do_step() {
auto time = std::chrono::system_clock::now();
last_core_step_distance = time - last_core_step_start;
last_core_step_start = time;
const lib94::warrior *result = lib94::single_step<true>();
core.mut.lock();
if (core_fading)
core.age_all();
core.add_new_writes(lib94::get_written_addresses());
core.add_new_reads(lib94::get_read_addresses());
core.add_new_executions(lib94::get_executed_addresses());
core.mut.unlock();
add_modified_for_instruction_view(lib94::get_written_addresses());
add_modified_for_instruction_view(lib94::get_read_addresses());
add_modified_for_instruction_view(lib94::get_executed_addresses());
lib94::clear_address_sets();
return result;
}
void bench_window::runner_main() {
std::chrono::system_clock::time_point last_step = std::chrono::system_clock::now();
core_mutex.lock();
while (!runner_stop_now) {
bool death = do_step() != 0;
if (runner_stop_on_death && death)
break;
if (runner_stop_on_win && death && lib94::alive_warrior_count() == 1)
break;
core_mutex.unlock();
runner_update_ui_dispatcher.emit();
if (!runner_hyperspeed)
std::this_thread::sleep_until(last_step + time_between_steps);
last_step = std::chrono::system_clock::now();
core_mutex.lock();
}
runner_stopping_dispatcher.emit();
core_mutex.unlock();
}
void bench_window::on_click_single_step() {
do_step();
update_ui();
}
void bench_window::on_click_new_round() {
lib94::clear_core({
.op = lib94::DAT,
.mod = lib94::F,
.amode = lib94::DIRECT,
.bmode = lib94::DIRECT,
.anumber = 0,
.bnumber = 0
});
lib94::init_round(warriors.data(), warriors.size());
core.mut.lock();
core.age_scale = std::pow(2.0 / 3.0, 1.0 / (float)warriors.size());
core.clear_all();
core.mut.unlock();
modified_addresses_for_instructions_view.clear();
for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i)
instructions_store->children()[i][instructions_columns.instruction]
= lib94::instruction_to_string(lib94::get_instruction(i));
update_ui();
}
void bench_window::on_click_start() {
runner_active = true;
update_ui();
runner_stop_now = false;
runner = std::thread(sigc::mem_fun(*this, &bench_window::runner_main));
}
void bench_window::on_click_stop() {
runner_stop_now = true;
}
void bench_window::on_toggle_core_fading() {
core_fading = core_fading_toggle.get_active();
}
void bench_window::on_toggle_hyperspeed() {
runner_hyperspeed = hyperspeed_toggle.get_active();
}
void bench_window::on_toggle_pause_on_death() {
runner_stop_on_death = pause_on_death_toggle.get_active();
}
void bench_window::on_toggle_pause_on_win() {
runner_stop_on_win = pause_on_win_toggle.get_active();
}
void bench_window::on_target_core_rate_changed() {
double target_rate = std::pow(10.0, target_core_rate_scale.get_value());
target_core_rate_label.set_text("target core rate (" + hz_to_string(target_rate) + ")");
time_between_steps = std::chrono::nanoseconds((int)(1000000000.0 / target_rate));
}
void bench_window::on_add_warrior_dialog_response(int response_id, Gtk::FileChooserDialog *dialog) {
if (response_id == Gtk::ResponseType::OK) {
Glib::RefPtr<Gio::File> file = dialog->get_file();
char *contents;
gsize length;
file->load_contents(contents, length);
try {
warriors.push_back(lib94::compile_warrior(std::string(contents, length)));
on_click_new_round();
}
catch (const lib94::compiler_exception &ex) {
Gtk::MessageDialog *md = new Gtk::MessageDialog(std::string("failed to compile: ") + ex.message + " on line " + std::to_string(ex.line_number));
md->set_transient_for(*this);
md->set_modal();
md->signal_response().connect([md](int) {delete md;});
md->show();
}
delete contents;
}
delete dialog;
}
void bench_window::on_click_add_warrior() {
Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog("select a warrior");
dialog->set_transient_for(*this);
dialog->set_modal();
dialog->signal_response().connect(sigc::bind(sigc::mem_fun(*this, &bench_window::on_add_warrior_dialog_response), dialog));
dialog->add_button("add", Gtk::ResponseType::OK);
dialog->add_button("cancel", Gtk::ResponseType::CANCEL);
Glib::RefPtr<Gtk::FileFilter> text_filter = Gtk::FileFilter::create();
text_filter->add_mime_type("text/plain");
text_filter->set_name("text files");
dialog->add_filter(text_filter);
Glib::RefPtr<Gtk::FileFilter> any_filter = Gtk::FileFilter::create();
any_filter->add_pattern("*");
any_filter->set_name("all files");
dialog->add_filter(any_filter);
dialog->show();
}
void bench_window::on_click_remove_warrior() {
auto iter = warrior_list_view.get_selection()->get_selected();
if (iter) {
while (!(*iter)[warrior_list_columns.warrior])
iter = iter->parent();
const lib94::warrior *w = (*iter)[warrior_list_columns.warrior];
for (auto i = warriors.begin(); i != warriors.end(); ++i)
if (*i == w) {
warriors.erase(i);
break;
}
on_click_new_round();
delete w;
}
}
void bench_window::update_buttons() {
new_round_button.set_sensitive(!runner_active && warriors.size() > 0);
single_step_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0);
start_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0);
stop_button.set_sensitive(runner_active);
add_warrior_button.set_sensitive(!runner_active);
remove_warrior_button.set_sensitive(!runner_active && warrior_list_view.get_selection()->get_selected());
warrior_list_view.get_selection()->set_mode(runner_active ? Gtk::SelectionMode::NONE : Gtk::SelectionMode::SINGLE);
core_render_label.set_text("core render: " + ns_to_string(core.last_draw_time));
core_rate_label.set_text("core rate: " + hz_to_string(1000000000.0 / (double)last_core_step_distance.count()));
}
void bench_window::update_ui() {
if (runner_active && runner_hyperspeed) {
update_buttons();
return;
}
if (runner_active)
core_mutex.lock();
update_buttons();
core.queue_draw();
warrior_list_store->clear();
for (const lib94::warrior *w : warriors) {
auto w_row = warrior_list_store->append();
(*w_row)[warrior_list_columns.warrior] = w;
(*w_row)[warrior_list_columns.warrior_name] = w->name;
auto procs = lib94::get_processes(w);
(*w_row)[warrior_list_columns.processes] = procs.size();
(*w_row)[warrior_list_columns.next_pc] = procs.size() ? std::to_string(procs[0]) : "";
}
for (lib94::number_t i : modified_addresses_for_instructions_view)
instructions_store->children()[i][instructions_columns.instruction]
= lib94::instruction_to_string(lib94::get_instruction(i));
modified_addresses_for_instructions_view.clear();
if (runner_active)
core_mutex.unlock();
}
void bench_window::on_runner_stopping() {
runner.join();
runner_active = false;
update_ui();
}