From 6405ad6b3f6246a7c6ada9b531188fae7d960e20 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Sun, 3 May 2026 20:41:59 +0200 Subject: [PATCH] allow to re-start arenas --- docs/requirements.md | 4 ++-- src/balancing/ArenaWidget.cpp | 3 +++ src/balancing/BalancingWindow.cpp | 14 ++++++++++---- src/balancing/BalancingWindow.h | 3 +++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index de37d5e..47991a1 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -285,7 +285,7 @@ A separate executable target (`balancing`) that links against `lib` but contains ### UI - REQ-BAL-UI-WINDOW: On startup the tool displays a window containing a "Start All" button at the top, followed by a scrollable vertical list of arena widgets, one per arena defined in `balancing.toml`. Simulations do not start automatically on startup. -- REQ-BAL-UI-START-ALL: The "Start All" button is placed above the scrollable arena list. Clicking it starts the simulation for every arena that has not yet been started. The button is disabled when all arenas are already running or finished. +- REQ-BAL-UI-START-ALL: The "Start All" button is placed above the scrollable arena list. Clicking it starts (or restarts) the simulation for every arena that is not currently running. The button is disabled when all arenas are currently running. - REQ-BAL-UI-WIDGET: Each arena widget displays the arena name and two columns (one per team). Each column shows the team name as a header, followed by a list of entries. The HQ is always the first entry in each column. Below the HQ, ship types are listed, followed by defence stations (if any). Each entry uses the format `surviving/total TypeName Llevel` — for example `2/3 Fighter L5` or `1/1 HQ L1`. The surviving count updates live as the simulation progresses. When the fight ends, the winning team's name header is prefixed with `[WON]`. -- REQ-BAL-UI-WIDGET-START: Each arena widget contains a "Start" button that starts the simulation for that arena. The button is disabled while the arena's simulation is running or after it has finished. +- REQ-BAL-UI-WIDGET-START: Each arena widget contains a "Start" button that starts the simulation for that arena. The button is disabled while the arena's simulation is running. When a finished arena's Start button is clicked, a fresh simulation is created and started (the widget resets to initial unit counts, the border returns to blue, and the previous results are replaced). - REQ-BAL-UI-WIDGET-BORDER: Each arena widget has a colored border indicating its state: grey when not yet started, blue while its simulation is running, and green when the fight has ended. diff --git a/src/balancing/ArenaWidget.cpp b/src/balancing/ArenaWidget.cpp index f2450a6..787920b 100644 --- a/src/balancing/ArenaWidget.cpp +++ b/src/balancing/ArenaWidget.cpp @@ -67,6 +67,7 @@ void ArenaWidget::buildLayout(const std::string& arenaName) void ArenaWidget::startSimulation() { m_running = true; + m_wasFinished = false; m_startButton->setEnabled(false); setStyleSheet("ArenaWidget { border: 2px solid #3366ff; padding: 8px; }"); } @@ -107,6 +108,8 @@ void ArenaWidget::updateStatus(const ArenaStatus& status) if (status.finished && !m_wasFinished) { m_wasFinished = true; + m_running = false; + m_startButton->setEnabled(true); setStyleSheet("ArenaWidget { border: 2px solid #33cc33; padding: 8px; }"); } } diff --git a/src/balancing/BalancingWindow.cpp b/src/balancing/BalancingWindow.cpp index 69cd8b0..b652edf 100644 --- a/src/balancing/BalancingWindow.cpp +++ b/src/balancing/BalancingWindow.cpp @@ -7,6 +7,8 @@ BalancingWindow::BalancingWindow(const BalancingConfig& balancingConfig, const GameConfig& gameConfig, QWidget* parent) : QWidget(parent) + , m_gameConfig(gameConfig) + , m_nextSeed(0) { setWindowTitle("DotaFactory — Balancing Tool"); resize(800, 600); @@ -26,14 +28,14 @@ BalancingWindow::BalancingWindow(const BalancingConfig& balancingConfig, contentLayout->setSpacing(8); contentLayout->setContentsMargins(8, 8, 8, 8); - unsigned int seed = 0; for (const ArenaConfig& arenaConfig : balancingConfig.arenas) { int index = static_cast(m_arenas.size()); ArenaEntry entry; + entry.config = arenaConfig; entry.simulation = std::make_unique( - gameConfig, arenaConfig, seed++); + m_gameConfig, arenaConfig, m_nextSeed++); entry.widget = new ArenaWidget(arenaConfig.name, scrollContent); contentLayout->addWidget(entry.widget); @@ -97,9 +99,13 @@ void BalancingWindow::startArena(int index) ArenaEntry& entry = m_arenas[index]; if (entry.worker.joinable()) { - return; + entry.simulation->requestStop(); + entry.worker.join(); } + entry.simulation = std::make_unique( + m_gameConfig, entry.config, m_nextSeed++); entry.widget->startSimulation(); + entry.widget->updateStatus(entry.simulation->status()); ArenaSimulation* sim = entry.simulation.get(); entry.worker = std::thread([sim]() { sim->run(); }); updateStartAllButton(); @@ -109,7 +115,7 @@ void BalancingWindow::updateStartAllButton() { for (ArenaEntry& entry : m_arenas) { - if (!entry.worker.joinable()) + if (!entry.worker.joinable() || entry.simulation->status().finished) { m_startAllButton->setEnabled(true); return; diff --git a/src/balancing/BalancingWindow.h b/src/balancing/BalancingWindow.h index 8243792..b58dc44 100644 --- a/src/balancing/BalancingWindow.h +++ b/src/balancing/BalancingWindow.h @@ -33,12 +33,15 @@ private: struct ArenaEntry { + ArenaConfig config; std::unique_ptr simulation; std::thread worker; ArenaWidget* widget; }; std::vector m_arenas; + const GameConfig& m_gameConfig; + unsigned int m_nextSeed; QPushButton* m_startAllButton; QTimer* m_pollTimer; };