Open jokal2 opened 4 years ago
You could "automate" with a CLI (see https://github.com/timhul/ClassicSim/issues/38). It would be interesting to do this in the GUI as well, but I hope that people understand the risks of possible permutation explosions (it'd be easy to calculate this and warn the user about it though).
Is the code in Test::test_queue
effectively all you need to run a full simulation? Having never touched Qt before some of the emit
and binding magic obscures exactly how the simulation is actually launched. If test_queue
is all you actually need I could tinker around and build off that.
Unfortunately no, that code only runs a single iteration (so e.g. 1 of 1000). It is effectively a sanity test that the Queue
class doesn't immediately crash when we try to run events. It does not handle any results from that single iteration either.
The class that is the heavy duty lifter of interest in order to simply run the sim is SimulationThreadPool
. It has dependencies on NumberCruncher
, EquipmentDb
, RandomAffixes
, and SimSettings
, but those are trivial to initialize as the default settings are fine.
const auto equipment_db = new EquipmentDb();
const auto random_affixes_db = new RandomAffixes();
const auto sim_settings = new SimSettings();
const auto number_cruncher = new NumberCruncher();
const auto thread_pool = new SimulationThreadPool(equipment_db, random_affixes_db, sim_settings, number_cruncher);
SimulationThreadPool
This class sets up the number of threads defined by SimSettings
(default is as many threads as physically available on the machine) and will given setup strings (extracted data from Character
instances) delegate the number of iterations to each thread (so, say 8 threads and 1000 iterations then this class makes sure each thread runs 125 iterations). The thread pool is responsible for scaling the number of threads up or down if the user changes it.
It is also responsible for waiting for each thread to finish and signals when all threads have completed their assigned iterations with the threads_finished()
signal.
Since it uses a signal threads_finished()
the SimulationThreadPool
object does require a Q_OBJECT class to run, which is currently GUIControl
. Q_OBJECT is a macro put in the class definition that tells QMake to generate a bunch of extra code that handles signals and slots. You should check how GUIControl
sets up SimulationThreadPool
and what the GUIControl::compile_thread_results
does (which is what is currently connected to SimulationThreadPool::threads_finished()
signal via the following line:
QObject::connect(thread_pool, SIGNAL(threads_finished()), this, SLOT(compile_thread_results()));
See GUIControl::runQuickSim()
for an example how the SimulationThreadPool
is being used to run the sim.
Results are collected in NumberCruncher
for all the involved Character
instances (remember that CSIM supports simulating an entire raid).
If you check GUIControl::compile_thread_results()
you can see many classes that are interested in getting results, but you seem to for this purpose only be interested in the personal dps.
qDebug() << number_cruncher->get_personal_dps(SimOption::Name::NoScale));
// Do a reset so it is ready for the next sim
number_cruncher->reset();
You will need a Q_OBJECT class because you'll need to connect SimulationThreadPool::threads_finished()
to your print of NumberCruncher::get_personal_dps()
.
.h definition:
#include <QObject>
class Foo : public QObject {
Q_OBJECT
public:
Foo(QObject* parent = nullptr);
~Foo();
void run();
public slots:
void compile_thread_results();
private:
EquipmentDb* equipment_db;
NumberCruncher* number_cruncher;
RandomAffixes* random_affixes_db;
SimSettings* sim_settings;
SimulationThreadPool* thread_pool;
}
.cpp implementation:
#include "Foo.h"
#include <QDebug>
#include "EquipmentDb.h"
#include "NumberCruncher.h"
#include "RandomAffixes.h"
#include "SimSettings.h"
#include "SimulationThreadPool.h"
Foo::Foo(QObject* parent):
QObject(parent),
equipment_db(new EquipmentDb()),
number_cruncher(new NumberCruncher()),
random_affixes_db(new RandomAffixes()),
sim_settings(new SimSettings())
{
thread_pool = new SimulationThreadPool(equipment_db, random_affixes_db, sim_settings, number_cruncher);
}
Foo::~Foo() {
delete thread_pool;
delete sim_settings;
delete random_affixes_db;
delete number_cruncher;
delete equipment_db;
}
Foo::run() {
// You can copy the Character setup from test_queue here.
// See GUIControl::runQuickSim() how to extract setup strings from Character and how to set up the QVector to pass to SimulationThreadPool.
// false below implies quick sim, no scaling of stat weights
// 1 further implies no scaling of stat weights, don't remember right now why both are used.
thread_pool->run_sim(setup_strings, false, sim_settings->get_combat_iterations_quick_sim(), 1);
}
Foo::compile_thread_results() {
qDebug() << number_cruncher->get_personal_dps(SimOption::Name::NoScale));
// Do a reset so it is ready for the next sim
number_cruncher->reset();
}
Now you can, from e.g. main.cpp
, invoke this class with Foo().run()
and you should get an output from the Character
setup you chose. Edit: No, you'll need a started event loop (which is done in main.cpp
via QApplication::exec()
. For simplicity I would put Foo().run()
somewhere in GUIControl
(must not be constructor, because event loop is not started yet).
I have not tested this, I'm sure there might be something I've missed here.
Side note: the Qt magic in this project is actually fairly limited since it only is used for cross-thread communication and objects that directly interact with the QML GUI. I've been in projects that used Qt stuff for everything but at that point the code reads like Qt++ rather than C++ with Qt enhancements.
Thanks for this!
I like to keep track of my current gear as a baseline and then have each item on my to-acquire list be simmed and tallied up. e.g.
etc. However, every time I make an item adjustment this entire list is invalidated and I'm required to manually recalculate and record every item in my list by hand. e.g.
It would be nice to have some sort of automation of this step.