summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-08-15 20:53:13 -0400
committerpommicket <pommicket@gmail.com>2025-08-15 20:53:13 -0400
commitbf75ca2b4ee5c00d53f7807d9feee23af281c9f6 (patch)
treeb8f6ac83da7c14ec2b5e625014e7709c48d9de92
parent0c53cb08d8032687887802f1352c6409326e5d6a (diff)
Solving, but something's broken
-rw-r--r--quacker/macondo.cpp41
-rw-r--r--quacker/macondo.h8
-rw-r--r--quacker/macondobackend.cpp75
-rw-r--r--quacker/macondobackend.h2
-rw-r--r--quacker/quacker.cpp26
-rw-r--r--quacker/quacker.h3
6 files changed, 116 insertions, 39 deletions
diff --git a/quacker/macondo.cpp b/quacker/macondo.cpp
index f92f89b..3048195 100644
--- a/quacker/macondo.cpp
+++ b/quacker/macondo.cpp
@@ -21,7 +21,7 @@ Macondo::Macondo(Quackle::Game *game) : View() {
execPath += "/apps/macondo/macondo";
initOptions = std::make_unique<MacondoInitOptions>(execPath);
m_backend = new MacondoBackend(game, *initOptions);
- connect(m_backend, SIGNAL(gotSimMoves(const Quackle::MoveList &)), this, SLOT(gotSimMoves(const Quackle::MoveList &)));
+ connect(m_backend, SIGNAL(gotMoves(const Quackle::MoveList &)), this, SLOT(gotMoves(const Quackle::MoveList &)));
m_solve = new QPushButton(tr("Solve"));
m_solve->setDisabled(true);
QGridLayout *layout = new QGridLayout(this);
@@ -36,26 +36,35 @@ Macondo::~Macondo() {
}
void Macondo::simulate() {
- if (m_backend->isRunning())
+ if (m_isSolving) {
+ // don't start a simulation if we're solving a (pre-)endgame
+ return;
+ }
+ if (isRunning())
stop();
clearMoves();
MacondoSimulateOptions options;
m_backend->simulate(options, m_movesFromKibitzer);
}
+bool Macondo::isRunning() const {
+ return m_backend->isRunning();
+}
+
void Macondo::solve() {
bool wasSolving = m_isSolving;
- if (m_backend->isRunning())
+ if (isRunning())
stop();
clearMoves();
if (wasSolving) {
- m_solve->setText(tr("Solve"));
+ emit stoppedSolver();
} else {
+ emit runningSolver();
MacondoSolveOptions options;
m_backend->solve(options);
- m_solve->setText(tr("Stop"));
m_isSolving = true;
}
+ updateSolveButton();
}
void Macondo::gameChanged(Quackle::Game *game) {
@@ -68,27 +77,37 @@ void Macondo::stop() {
m_backend->stop();
m_anyUpdates = false;
m_isSolving = false;
+ updateSolveButton();
}
bool Macondo::useForSimulation() const {
return m_useMacondo->isChecked();
}
-void Macondo::gotSimMoves(const Quackle::MoveList &moves) {
+void Macondo::gotMoves(const Quackle::MoveList &moves) {
+ printf("aaa moves\n");
m_moves = moves;
m_anyUpdates = true;
}
void Macondo::positionChanged(const Quackle::GamePosition *position) {
- if (!m_backend->isRunning()) {
+ if (!isRunning()) {
// perhaps new moves were generated
m_movesFromKibitzer = position->moves();
- }
- int tilesLeft = position->unseenBag().size();
- if (!position->gameOver() && tilesLeft <= 7) {
+ }
+ m_tilesUnseen = position->gameOver() ? 0 : position->unseenBag().size();
+ updateSolveButton();
+}
+
+// update "Solve" button text and enabledness*
+void Macondo::updateSolveButton() {
+ if (isRunning()) {
+ m_solve->setText(tr("Stop"));
+ m_solve->setDisabled(false);
+ } else if (m_tilesUnseen > 0 && m_tilesUnseen <= 7) {
m_solve->setText(tr("Solve endgame"));
m_solve->setDisabled(false);
- } else if (!position->gameOver() && tilesLeft <= 10) {
+ } else if (m_tilesUnseen > 7 && m_tilesUnseen <= 10) {
m_solve->setText(tr("Solve pre-endgame"));
m_solve->setDisabled(false);
} else {
diff --git a/quacker/macondo.h b/quacker/macondo.h
index 4fcf758..638456f 100644
--- a/quacker/macondo.h
+++ b/quacker/macondo.h
@@ -27,20 +27,26 @@ public:
m_anyUpdates = false;
return any;
}
+ bool isRunning() const;
+signals:
+ void runningSolver();
+ void stoppedSolver();
public slots:
void simulate();
void solve();
void gameChanged(Quackle::Game *game) override;
void positionChanged(const Quackle::GamePosition *position) override;
private slots:
- void gotSimMoves(const Quackle::MoveList &moves);
+ void gotMoves(const Quackle::MoveList &moves);
private:
+ void updateSolveButton();
QCheckBox *m_useMacondo;
QPushButton *m_solve;
Quackle::Game *m_game;
MacondoBackend *m_backend;
Quackle::MoveList m_moves;
Quackle::MoveList m_movesFromKibitzer;
+ int m_tilesUnseen = 93;
int m_viewingPlyNumber = 0;
bool m_anyUpdates = false;
bool m_isSolving = false;
diff --git a/quacker/macondobackend.cpp b/quacker/macondobackend.cpp
index 5837092..59db5cf 100644
--- a/quacker/macondobackend.cpp
+++ b/quacker/macondobackend.cpp
@@ -148,6 +148,24 @@ static double parseEquity(const string &equityString) {
return equity;
}
+static Quackle::Move createPlaceMove(const string &placement, const string &prettyTiles) {
+ string dotDescription;
+ for (size_t i = 0; i < prettyTiles.size();) {
+ size_t j = prettyTiles.find("(", i);
+ if (j == string::npos) {
+ dotDescription += prettyTiles.substr(i);
+ break;
+ }
+ dotDescription += prettyTiles.substr(i, j) + ".";
+ i = prettyTiles.find(")", j);
+ if (i == string::npos) throw "mismatched parentheses";
+ i++;
+ }
+ auto move = Quackle::Move::createPlaceMove(placement, QUACKLE_ALPHABET_PARAMETERS->encode(dotDescription));
+ move.setPrettyTiles(QUACKLE_ALPHABET_PARAMETERS->encode(prettyTiles));
+ return move;
+}
+
// parse move from Macondo sim
static Quackle::Move extractSimMove(const string &play) {
vector<string> words = splitWords(play);
@@ -179,24 +197,11 @@ static Quackle::Move extractSimMove(const string &play) {
} else {
// normal play
const string &placement = words[0];
- const string &description = words[1];
+ const string &prettyTiles = words[1];
const string &scoreString = words[2 + !plays7];
const string &winString = words[3 + !plays7];
const string &equityString = words[4 + !plays7];
- string dotDescription;
- for (size_t i = 0; i < description.size();) {
- size_t j = description.find("(", i);
- if (j == string::npos) {
- dotDescription += description.substr(i);
- break;
- }
- dotDescription += description.substr(i, j) + ".";
- i = description.find(")", j);
- if (i == string::npos) throw "mismatched parentheses";
- i++;
- }
- Quackle::Move move = Quackle::Move::createPlaceMove(placement, QUACKLE_ALPHABET_PARAMETERS->encode(dotDescription));
- move.setPrettyTiles(QUACKLE_ALPHABET_PARAMETERS->encode(description));
+ Quackle::Move move = createPlaceMove(placement, prettyTiles);
move.score = parseScore(scoreString);
move.win = parseWinRate(winString);
move.equity = parseEquity(equityString);
@@ -246,9 +251,36 @@ static bool extractEndgameMove(QByteArray &processOutput, Quackle::Move &move) {
seqStart += bestSeqMarker.length();
string sequenceStr(processOutput.data() + seqStart, processOutput.size() - seqStart);
vector<string> sequence = splitLines(sequenceStr);
- printf("got sequence:\n");
- for (const string &moveStr: sequence) {
- printf(" | %s\n",moveStr.c_str());
+ // TODO: do something with rest of moves? or just extract first move?
+ const string &firstMove = sequence[0];
+ vector<string> moveWords = splitWords(firstMove);
+ if (moveWords.size() == 4) {
+ const string &placement = moveWords[1];
+ const string &prettyTiles = moveWords[2];
+ const string &scoreInParentheses = moveWords[3];
+ move = createPlaceMove(placement, prettyTiles);
+ int score = 0;
+ bool scoreIsValid = scoreInParentheses[0] == '(' && scoreInParentheses.back() == ')';
+ try {
+ size_t scoreLen = 0;
+ score = std::stoi(scoreInParentheses.substr(1), &scoreLen);
+ scoreIsValid = scoreLen == scoreInParentheses.length() - 2;
+ } catch (const std::invalid_argument &) {
+ } catch (const std::out_of_range &) {
+ }
+ if (scoreIsValid) {
+ move.score = score;
+ } else {
+ qWarning("bad move score syntax: %s", scoreInParentheses.c_str());
+ }
+ processOutput.clear();
+ return true;
+ } else if (moveWords.size() == 3 && moveWords[1] == "Pass" && moveWords[2] == "(0)") {
+ move = Quackle::Move::createPassMove();
+ processOutput.clear();
+ return true;
+ } else {
+ qWarning("bad move syntax: %s", firstMove.c_str());
}
processOutput.clear();
return false;
@@ -274,7 +306,7 @@ void MacondoBackend::timer() {
if (!moves.empty()) {
// at this point the GCG is definitely fully loaded
removeTempGCG();
- emit gotSimMoves(moves);
+ emit gotMoves(moves);
}
}
break;
@@ -290,7 +322,10 @@ void MacondoBackend::timer() {
}
Quackle::Move move;
if (extractEndgameMove(m_processOutput, move)) {
- std::cout << "Move: " << move << std::endl;
+ Quackle::MoveList list;
+ list.push_back(move);
+ printf("EMIT\n");
+ emit gotMoves(list);
}
}
break;
diff --git a/quacker/macondobackend.h b/quacker/macondobackend.h
index dcc161d..ebb68d8 100644
--- a/quacker/macondobackend.h
+++ b/quacker/macondobackend.h
@@ -31,7 +31,7 @@ public:
// stop current Macondo analysis
void stop();
signals:
- void gotSimMoves(const Quackle::MoveList &moves);
+ void gotMoves(const Quackle::MoveList &moves);
private slots:
void processStarted();
void processFinished(int, QProcess::ExitStatus);
diff --git a/quacker/quacker.cpp b/quacker/quacker.cpp
index b54e364..3d8d8d4 100644
--- a/quacker/quacker.cpp
+++ b/quacker/quacker.cpp
@@ -1052,15 +1052,19 @@ void TopLevel::ensureUpToDateSimulatorMoveList()
void TopLevel::simulate(bool startSimulation)
{
m_simulateAction->setChecked(startSimulation);
+
+ if (m_macondo->isRunning() && !startSimulation) {
+ // stop Macondo
+ m_macondo->stop();
+ m_simulationTimer->stop();
+ return;
+ }
// it's not so useful to have sim control show/hide
// like this
//m_simulatorWidget->setVisible(startSimulation);
if (m_macondo->useForSimulation()) {
- if (startSimulation)
- m_macondo->simulate();
- else
- m_macondo->stop();
+ m_macondo->simulate();
}
if (startSimulation)
{
@@ -1076,6 +1080,15 @@ void TopLevel::simulateToggled(bool startSimulation)
if (!m_game->hasPositions())
return;
+ if (startSimulation) {
+ // this function is called for the Macondo solver as well,
+ // so make sure user can stop solver through button in top bar.
+ m_simulateAction->setEnabled(true);
+ } else {
+ // restore previous enabledness* of Simulate action
+ m_simulateAction->setEnabled(!m_game->currentPosition().moves().empty());
+ }
+
simulate(startSimulation);
if (startSimulation)
@@ -1254,8 +1267,9 @@ void TopLevel::incrementSimulation()
if (!m_simulateAction->isChecked())
return;
- if (m_macondo->useForSimulation()) {
+ if (m_macondo->isRunning()) {
if (m_macondo->anyUpdates()) {
+ printf("aaa update\n");
updateMoveViews();
updateSimViews();
}
@@ -2026,6 +2040,8 @@ void TopLevel::createWidgets()
m_macondo = new Macondo(m_game);
plugIntoMatrix(m_macondo);
plugIntoPositionMatrix(m_macondo);
+ connect(m_macondo, SIGNAL(runningSolver()), this, SLOT(simulate()));
+ connect(m_macondo, SIGNAL(stoppedSolver()), this, SLOT(stopSimulation()));
m_tabWidget = new QTabWidget;
m_tabWidget->addTab(m_history, tr("Histor&y"));
diff --git a/quacker/quacker.h b/quacker/quacker.h
index 399deee..9570890 100644
--- a/quacker/quacker.h
+++ b/quacker/quacker.h
@@ -116,7 +116,8 @@ public slots:
void htmlReport();
void graphicalReport();
void commitTopChoice();
- void simulate(bool startSimulation);
+ void simulate(bool startSimulation = true);
+ inline void stopSimulation() { simulate(false); }
void simulateToggled(bool startSimulation);
void clearSimulationResults();