File indexing completed on 2025-02-16 06:56:39
0001 /* 0002 This file is part of the KDE games kwin4 program 0003 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 /* Note: The AI engine of kwin4 does on purpose not implement a perfect 0009 * Connect-4(tm) engine but tries to play more human as playing against 0010 * a perfect engine is only frustrating. Connect four is proven to 0011 * always win for the first player with perfect play. 0012 * see e.g. the Velena AI Engine http://web.archive.org/web/20180219021151/http://www.ce.unipr.it/~gbe/velena.html 0013 */ 0014 0015 #include "kwin4doc.h" 0016 0017 // own 0018 #include "config-src.h" 0019 #include "kfourinline_debug.h" 0020 #include "kwin4gamesequence.h" 0021 #include "kwin4view.h" 0022 #include "prefs.h" 0023 #include "score.h" 0024 #include "scoresprite.h" 0025 #include "ui_statuswidget.h" 0026 // KF 0027 #include <KLocalizedString> 0028 // Qt 0029 #include <QDir> 0030 #include <QStandardPaths> 0031 #include <QTimer> 0032 0033 #define FIELD_SIZE_X 7 0034 #define FIELD_SIZE_Y 6 0035 0036 // Constructor 0037 KWin4Doc::KWin4Doc(QWidget *parent) 0038 : KGame(1234, parent) 0039 , pView() 0040 , mHintProcess() 0041 { 0042 setGameSequence(new KWin4GameSequence(this)); 0043 mStatus = new Score(parent); 0044 0045 connect(this, &KWin4Doc::signalPropertyChanged, this, &KWin4Doc::gamePropertyChanged); 0046 0047 dataHandler()->Debug(); 0048 // qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy(); 0049 setPolicy(KGame::PolicyDirty, true); 0050 0051 // qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy(); 0052 0053 // Game design 0054 setMaxPlayers(2); 0055 setMinPlayers(2); 0056 0057 // Game initialization 0058 mField.resize(42); 0059 0060 // **************************************** 0061 // NOTE: Do not i18n the strings here. They 0062 // are for debugging only 0063 // **************************************** 0064 0065 // The field array needs not be updated as any move will change it 0066 // Careful only in new resetGame! Maybe unlocal it there! 0067 // mField.setPolicy(KGamePropertyBase::PolicyLocal); 0068 mField.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mField")); 0069 0070 mFieldFilled.resize(7); 0071 mHistory.resize(43); 0072 mHistory.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mHistory")); 0073 0074 mAmzug.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mAmzug")); 0075 mCurrentMove.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mCurrentMove")); 0076 mMaxMove.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mMaxMove")); 0077 mFieldFilled.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mFieldFilled")); 0078 mHistoryCnt.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mHistoryCnt")); 0079 mLastColumn.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mLastColumn")); 0080 mLastHint.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mLastHint")); 0081 mLastColour.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mLastColour")); 0082 mScore.registerData(dataHandler(), KGamePropertyBase::PolicyLocal, QStringLiteral("mScore")); 0083 0084 // game startup parameter 0085 mStartPlayer = Yellow; 0086 mStartPlayer.registerData(dataHandler(), KGamePropertyBase::PolicyDirty, QStringLiteral("mStartPlayer")); 0087 setCurrentPlayer((COLOUR)mStartPlayer.value()); 0088 if (global_debug > 1) 0089 qCDebug(KFOURINLINE_LOG) << "amZug policy=" << mAmzug.policy(); 0090 0091 mPlayedBy[Yellow] = KGameIO::MouseIO; 0092 mPlayedBy[Red] = KGameIO::MouseIO; 0093 0094 // AI support 0095 mAIValues.resize(42); 0096 0097 // last in init 0098 resetGame(false); 0099 0100 setGameStatus(Intro); 0101 0102 // Listen to network 0103 connect(this, &KWin4Doc::signalMessageUpdate, this, &KWin4Doc::networkMessageUpdate); 0104 connect(this, &KWin4Doc::signalClientJoinedGame, this, &KWin4Doc::clientConnected); 0105 0106 // Change global KGame policy 0107 // dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false); 0108 dataHandler()->Debug(); 0109 } 0110 0111 // Destructor 0112 KWin4Doc::~KWin4Doc() 0113 { 0114 qCDebug(KFOURINLINE_LOG) << "~KWin4Doc()"; 0115 delete mHintProcess; 0116 delete mStatus; 0117 qCDebug(KFOURINLINE_LOG) << "~KWin4Doc() done"; 0118 } 0119 0120 // Player initialization 0121 void KWin4Doc::initPlayers() 0122 { 0123 // Create yellow 0124 KWin4Player *yellow = (KWin4Player *)createPlayer(1, mPlayedBy[Yellow], false); 0125 yellow->setUserId(Yellow); 0126 yellow->setName(Prefs::name1()); 0127 addPlayer(yellow); 0128 setPlayedBy(Yellow, mPlayedBy[Yellow]); 0129 0130 // Create Red 0131 KWin4Player *red = (KWin4Player *)createPlayer(1, mPlayedBy[Red], false); 0132 red->setUserId(Red); 0133 red->setName(Prefs::name1()); 0134 addPlayer(red); 0135 setPlayedBy(Red, mPlayedBy[Red]); 0136 } 0137 0138 // Set the view to the doc 0139 void KWin4Doc::setView(KWin4View *view) 0140 { 0141 pView = view; 0142 connect(pView, &KWin4View::signalMoveDone, this, &KWin4Doc::moveDone); 0143 } 0144 0145 // Returns colour on the game board 0146 COLOUR KWin4Doc::getColour(int x, int y) 0147 { 0148 return (COLOUR)mField.at(x + y * FIELD_SIZE_X); 0149 } 0150 0151 // Set the colour on the game board 0152 void KWin4Doc::setColour(int x, int y, COLOUR c) 0153 { 0154 if (x < 0 || x >= FIELD_SIZE_X || y < 0 || y >= FIELD_SIZE_Y) { 0155 qCCritical(KFOURINLINE_LOG) << "ERROR: setColour on wrong position" << x << " " << y; 0156 return; 0157 } 0158 mField.setAt(x + y * FIELD_SIZE_X, c); 0159 } 0160 0161 // Reset the whole game (and the view) 0162 void KWin4Doc::resetGame(bool initview) 0163 { 0164 // Reset field 0165 for (int x = 0; x < FIELD_SIZE_X; ++x) { 0166 for (int y = FIELD_SIZE_Y - 1; y >= 0; --y) { 0167 setColour(x, y, Nobody); 0168 } 0169 } 0170 mFieldFilled.fill(0); 0171 0172 // Reset game vars 0173 mHistoryCnt = 0; 0174 mCurrentMove = 0; 0175 mMaxMove = 0; 0176 mLastColumn = -1; 0177 mLastColour = Nobody; 0178 setScore(0); 0179 mLastHint = -1; 0180 0181 // Reset the view 0182 if (initview) { 0183 pView->initGame(mStatus); 0184 } 0185 0186 // Who starts this game 0187 setCurrentPlayer((COLOUR)mStartPlayer.value()); 0188 } 0189 0190 // Set current player to setTurn true 0191 void KWin4Doc::activateCurrentPlayer() 0192 { 0193 if (global_debug > 1) 0194 qCDebug(KFOURINLINE_LOG) << "Setting the current player to turn"; 0195 getPlayer(getCurrentPlayer())->setTurn(true, true); 0196 } 0197 0198 // End a game. Update statistic and forward end game to view. 0199 void KWin4Doc::endGame(TABLE mode) 0200 { 0201 setGameStatus(End); 0202 // TODO pView->clearError(); 0203 pView->endGame(); 0204 0205 // Increase game statistics 0206 KWin4Player *yellow = getPlayer(Yellow); 0207 KWin4Player *red = getPlayer(Red); 0208 switch (mode) { 0209 case TWin: 0210 yellow->incWin(); 0211 red->incLost(); 0212 break; 0213 case TLost: 0214 yellow->incLost(); 0215 red->incWin(); 0216 break; 0217 case TRemis: 0218 yellow->incRemis(); 0219 red->incRemis(); 0220 break; 0221 default: 0222 // Only break if moves have been made 0223 if (mMaxMove > 0) { 0224 yellow->incBrk(); 0225 red->incBrk(); 0226 } 0227 break; 0228 } 0229 } 0230 0231 // Indication that a move has been visually done 0232 void KWin4Doc::moveDone(int /*mode*/) 0233 { 0234 if (playerCount() > 1) { 0235 playerInputFinished(getPlayer(getCurrentPlayer())); 0236 } 0237 0238 // TODO pView->clearError(); 0239 } 0240 0241 // Calculate the next players to turn. Here the players just swap. 0242 KPlayer *KWin4Doc::doNextPlayer(KPlayer *last, bool /*exclusive*/) 0243 { 0244 if (global_debug > 1) 0245 qCDebug(KFOURINLINE_LOG) << "doNextPlayer last=" << last->id() << "admin=" << isAdmin(); 0246 0247 // Should be enough if the admin sets the turn 0248 if (last->userId() == Yellow) 0249 setCurrentPlayer(Red); 0250 else 0251 setCurrentPlayer(Yellow); 0252 if (global_debug > 1) 0253 qCDebug(KFOURINLINE_LOG) << " Current set to " << getCurrentPlayer(); 0254 if (isAdmin()) 0255 getPlayer(getCurrentPlayer())->setTurn(true, true); 0256 Q_EMIT signalNextPlayer(int(getCurrentPlayer())); 0257 return getPlayer(getCurrentPlayer()); 0258 } 0259 0260 // Performs a game move on the given x position. Just calls makeMove() 0261 // and transforms the return value so that true indicates a valid move. 0262 bool KWin4Doc::doMove(int x, int id) 0263 { 0264 if (global_debug > 1) 0265 qCDebug(KFOURINLINE_LOG) << " KWin4Doc::Move pos=" << x << " id=" << id << " "; 0266 0267 return (makeMove(x, 0) == GNormal); 0268 } 0269 0270 // Make a game move on the given position. Display it in the view. 0271 // mode=0 normal move, =1: redo move 0272 MOVESTATUS KWin4Doc::makeMove(int x, int mode) 0273 { 0274 if (x < 0 || x >= FIELD_SIZE_X) { 0275 qCDebug(KFOURINLINE_LOG) << "ERROR: makeMove auf falsche Position" << x; 0276 return GNotAllowed; 0277 } 0278 0279 int y = mFieldFilled.at(x); 0280 0281 if (y >= FIELD_SIZE_Y) { 0282 return GIllMove; // no space left in column 0283 } 0284 0285 if (mLastHint >= 0) { 0286 int hy; 0287 hy = mFieldFilled.at(mLastHint); 0288 setColour(mLastHint, hy, Nobody); 0289 mLastHint = -1; 0290 } 0291 if (mode == Tip) { 0292 mLastHint = x; 0293 setColour(x, y, Tip); 0294 return GTip; // no real move 0295 } 0296 0297 mFieldFilled.setAt(x, mFieldFilled.at(x) + 1); 0298 setColour(x, y, getCurrentPlayer()); 0299 0300 mHistory.setAt(getHistoryCnt(), x); 0301 mHistoryCnt = mHistoryCnt.value() + 1; 0302 0303 mLastColour = getCurrentPlayer(); 0304 // if (getCurrentPlayer()==Yellow) setCurrentPlayer(Red); 0305 // else setCurrentPlayer(Yellow); 0306 0307 mCurrentMove = mCurrentMove.value() + 1; 0308 0309 // only if a real move isdone the maxmove is raised 0310 if (mode == 0) 0311 mMaxMove = mCurrentMove.value(); 0312 mLastColumn = x; 0313 0314 // Show graphics 0315 pView->displayMove(x, y, mLastColour, x, mLastColour, mCurrentMove - 1, mode == 1 ? false : true); 0316 0317 return GNormal; 0318 } 0319 0320 // Undo a move. 0321 bool KWin4Doc::undoMove() 0322 { 0323 if (getHistoryCnt() < 1) 0324 return false; 0325 0326 if (mLastHint >= 0) { 0327 int hy; 0328 hy = mFieldFilled.at(mLastHint); 0329 setColour(mLastHint, hy, Nobody); 0330 mLastHint = -1; 0331 } 0332 // qCDebug(KFOURINLINE_LOG) << "Undo no="<<mHistoryCnt.value(); 0333 mHistoryCnt = mHistoryCnt.value() - 1; 0334 int x = mHistory.at(getHistoryCnt()); 0335 mFieldFilled.setAt(x, mFieldFilled.at(x) - 1); 0336 int y = mFieldFilled.at(x); 0337 // qCDebug(KFOURINLINE_LOG) << "Undo x="<<x << "y=" <<y; 0338 setColour(x, y, Nobody); 0339 // We have to remove the piece as well... 0340 0341 mLastColour = getCurrentPlayer(); 0342 if (getCurrentPlayer() == Yellow) 0343 setCurrentPlayer(Red); 0344 else 0345 setCurrentPlayer(Yellow); 0346 mCurrentMove = mCurrentMove.value() - 1; 0347 0348 // Display move and arrow history 0349 if (getHistoryCnt() > 0) { 0350 pView->displayMove(x, y, Nobody, mHistory.at(getHistoryCnt() - 1), mLastColour.value(), mCurrentMove, false); 0351 } else { 0352 pView->displayMove(x, y, Nobody, -1, Nobody, mCurrentMove, false); 0353 } 0354 0355 if (getHistoryCnt() > 0) 0356 mLastColumn = mHistory.at(getHistoryCnt() - 1); 0357 else 0358 mLastColumn = -1; 0359 0360 setScore(0); 0361 0362 return true; 0363 } 0364 0365 // Redo a move 0366 bool KWin4Doc::redoMove() 0367 { 0368 if (getHistoryCnt() >= mMaxMove) 0369 return false; 0370 0371 int x = mHistory.at(getHistoryCnt()); 0372 // qCDebug(KFOURINLINE_LOG) << "Redo x=" << x; 0373 makeMove(x, 1); 0374 if (getCurrentPlayer() == Yellow) 0375 setCurrentPlayer(Red); 0376 else 0377 setCurrentPlayer(Yellow); 0378 setScore(0); 0379 return true; 0380 } 0381 0382 // Set the name of the player of the given color 0383 void KWin4Doc::setName(COLOUR col, const QString &n) 0384 { 0385 getPlayer(col)->setName(n); 0386 } 0387 0388 // Retrieve the name of the player of the given color 0389 QString KWin4Doc::getName(COLOUR col) 0390 { 0391 return getPlayer(col)->name(); 0392 } 0393 0394 // Returns the all time statistics for player of given color 0395 // The mode determines what statistics to access. 0396 int KWin4Doc::getStatistic(COLOUR col, TABLE mode) 0397 { 0398 KWin4Player *player = getPlayer(col); 0399 switch (mode) { 0400 case TWin: 0401 return player->win(); 0402 break; 0403 case TRemis: 0404 return player->remis(); 0405 break; 0406 case TLost: 0407 return player->lost(); 0408 break; 0409 case TBrk: 0410 return player->brk(); 0411 break; 0412 case TSum: 0413 return (player->win() + player->remis() + player->lost()); 0414 default: 0415 break; 0416 } 0417 return 0; 0418 } 0419 0420 // Retrieve the color of the i-th player. Player 0 is the start 0421 // player and player 1 the follow up player. 0422 COLOUR KWin4Doc::getPlayerColour(int player) 0423 { 0424 if (player == 0) 0425 return (COLOUR)mStartPlayer.value(); 0426 0427 if (mStartPlayer.value() == Yellow) 0428 return Red; 0429 else 0430 return Yellow; 0431 } 0432 0433 // Check whether the current game has a game over situation 0434 // return -1: remis, 1:won, 0: continue 0435 int KWin4Doc::doCheckGameOver(KPlayer *p) 0436 { 0437 if (global_debug > 1) 0438 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::doCheckGameOver::" << p->userId(); 0439 return checkGameOver(mLastColumn, (COLOUR)(mLastColour.value())); 0440 } 0441 0442 // Check whether the current game has a game over situation 0443 // return -1: remis, 1:won, 0: continue 0444 int KWin4Doc::checkGameOver(int x, COLOUR col) 0445 { 0446 int y, i; 0447 COLOUR c; 0448 int star = 1; 0449 COLOUR winc = Nobody; 0450 0451 // Check vertical up 0452 int flag = 0; 0453 for (i = 0; i < 4; ++i) { 0454 y = mFieldFilled.at(x) - 1 - i; 0455 if (y >= 0) { 0456 c = getColour(x, y); 0457 if (c == col) 0458 ++flag; 0459 } 0460 } 0461 if (flag >= 4) { 0462 // Store win fields 0463 for (i = 0; i < 4; ++i) { 0464 y = mFieldFilled.at(x) - 1 - i; 0465 pView->displayStar(x, y, star++); 0466 winc = getColour(x, y); 0467 } 0468 return 1; 0469 } else if (flag >= 4) 0470 return 1; 0471 0472 int xx; 0473 // Check horizontal to the right 0474 y = mFieldFilled.at(x) - 1; 0475 flag = 0; 0476 for (i = -3; i <= 3 && flag < 4; ++i) { 0477 xx = x + i; 0478 if (xx >= 0 && xx < FIELD_SIZE_X) { 0479 c = getColour(xx, y); 0480 if (c == col) 0481 ++flag; 0482 else 0483 flag = 0; 0484 } 0485 } 0486 if (flag >= 4) { 0487 // Store win fields 0488 y = mFieldFilled.at(x) - 1; 0489 winc = getColour(x, y); 0490 int cnt = 0; 0491 for (i = 0; i < 4; ++i) { 0492 xx = x + i; 0493 if (xx >= 0 && xx < FIELD_SIZE_X) { 0494 if (getColour(xx, y) != winc) 0495 break; 0496 pView->displayStar(xx, y, star++); 0497 ++cnt; 0498 } else 0499 break; 0500 } 0501 for (i = -1; i > -4 && cnt < 4; --i) { 0502 xx = x + i; 0503 if (xx >= 0 && xx < FIELD_SIZE_X) { 0504 if (getColour(xx, y) != winc) 0505 break; 0506 pView->displayStar(xx, y, star++); 0507 ++cnt; 0508 } else 0509 break; 0510 } 0511 return 1; 0512 } else if (flag >= 4) 0513 return 1; 0514 0515 // Check dy+ 0516 flag = 0; 0517 for (i = -3; i <= 3 && flag < 4; ++i) { 0518 xx = x + i; 0519 if (xx >= 0 && xx < FIELD_SIZE_X) { 0520 y = mFieldFilled.at(x) - 1 - i; 0521 if (y >= 0 && y < FIELD_SIZE_Y) { 0522 c = getColour(xx, y); 0523 if (c == col) 0524 ++flag; 0525 else 0526 flag = 0; 0527 } 0528 } 0529 } 0530 if (flag >= 4) { 0531 // Store win fields 0532 y = mFieldFilled.at(x) - 1; 0533 winc = getColour(x, y); 0534 int cnt = 0; 0535 for (i = 0; i < 4; ++i) { 0536 xx = x + i; 0537 if (xx >= 0 && xx < FIELD_SIZE_X) { 0538 y = mFieldFilled.at(x) - 1 - i; 0539 if (y < 0) 0540 break; 0541 if (getColour(xx, y) != winc) 0542 break; 0543 pView->displayStar(xx, y, star++); 0544 ++cnt; 0545 } else 0546 break; 0547 } 0548 for (i = -1; i > -4 && cnt < 4; --i) { 0549 xx = x + i; 0550 if (xx >= 0 && xx < FIELD_SIZE_X) { 0551 y = mFieldFilled.at(x) - 1 - i; 0552 if (y >= FIELD_SIZE_Y) 0553 break; 0554 if (getColour(xx, y) != winc) 0555 break; 0556 pView->displayStar(xx, y, star++); 0557 ++cnt; 0558 } else 0559 break; 0560 } 0561 return 1; 0562 } else if (flag >= 4) 0563 return 1; 0564 0565 // Check dy- 0566 flag = 0; 0567 for (i = -3; i <= 3 && flag < 4; ++i) { 0568 xx = x + i; 0569 if (xx >= 0 && xx < FIELD_SIZE_X) { 0570 y = mFieldFilled.at(x) - 1 + i; 0571 if (y >= 0 && y < FIELD_SIZE_Y) { 0572 c = getColour(xx, y); 0573 if (c == col) 0574 ++flag; 0575 else 0576 flag = 0; 0577 } 0578 } 0579 } 0580 if (flag >= 4) { 0581 // Store win fields 0582 y = mFieldFilled.at(x) - 1; 0583 winc = getColour(x, y); 0584 int cnt = 0; 0585 for (i = 0; i < 4; ++i) { 0586 xx = x + i; 0587 if (xx >= 0 && xx < FIELD_SIZE_X) { 0588 y = mFieldFilled.at(x) - 1 + i; 0589 if (y >= FIELD_SIZE_Y) 0590 break; 0591 if (getColour(xx, y) != winc) 0592 break; 0593 pView->displayStar(xx, y, star++); 0594 ++cnt; 0595 } else 0596 break; 0597 } 0598 for (i = -1; i > -4 && cnt < 4; --i) { 0599 xx = x + i; 0600 if (xx >= 0 && xx < FIELD_SIZE_X) { 0601 y = mFieldFilled.at(x) - 1 + i; 0602 if (y < 0) 0603 break; 0604 if (getColour(xx, y) != winc) 0605 break; 0606 pView->displayStar(xx, y, star++); 0607 ++cnt; 0608 } else 0609 break; 0610 } 0611 return 1; 0612 } else if (flag >= 4) 0613 return 1; 0614 0615 if (mCurrentMove >= 42) 0616 return -1; 0617 0618 return 0; 0619 } 0620 0621 // Reset all the player stats 0622 void KWin4Doc::resetStatistic() 0623 { 0624 getPlayer(Yellow)->resetStats(); 0625 getPlayer(Red)->resetStats(); 0626 } 0627 0628 // Set computer AI score value 0629 void KWin4Doc::setScore(long value) 0630 { 0631 mScore.setValue(value); 0632 } 0633 0634 // Load settings from Prefs 0635 void KWin4Doc::loadSettings() 0636 { 0637 qCDebug(KFOURINLINE_LOG) << "++++ KWin4Doc::loadSettings() "; 0638 qCDebug(KFOURINLINE_LOG) << "Level:" << Prefs::level(); 0639 qCDebug(KFOURINLINE_LOG) << "Name:" << Prefs::name1(); 0640 qCDebug(KFOURINLINE_LOG) << "Name2:" << Prefs::name2(); 0641 qCDebug(KFOURINLINE_LOG) << "input0mouse:" << Prefs::input0mouse(); 0642 qCDebug(KFOURINLINE_LOG) << "input0key:" << Prefs::input0key(); 0643 qCDebug(KFOURINLINE_LOG) << "input0ai:" << Prefs::input0ai(); 0644 qCDebug(KFOURINLINE_LOG) << "input1mouse:" << Prefs::input1mouse(); 0645 qCDebug(KFOURINLINE_LOG) << "input1key:" << Prefs::input1key(); 0646 qCDebug(KFOURINLINE_LOG) << "input1ai:" << Prefs::input1ai(); 0647 qCDebug(KFOURINLINE_LOG) << "start red:" << Prefs::startcolourred(); 0648 qCDebug(KFOURINLINE_LOG) << "start yellow" << Prefs::startcolouryellow(); 0649 qCDebug(KFOURINLINE_LOG) << "Learning " << Prefs::learning(); 0650 qCDebug(KFOURINLINE_LOG) << "Lock " << Prefs::ailock(); 0651 0652 // Store level for score sprite display 0653 mStatus->setLevel(Prefs::level(), 0); 0654 mStatus->setLevel(Prefs::level(), 1); 0655 0656 setName(Yellow, Prefs::name1()); 0657 setName(Red, Prefs::name2()); 0658 0659 KGameIO::IOMode mode = KGameIO::MouseIO; 0660 0661 if (Prefs::input0mouse()) 0662 mode = KGameIO::MouseIO; 0663 else if (Prefs::input0key()) 0664 mode = KGameIO::KeyIO; 0665 else if (Prefs::input0ai()) 0666 mode = KGameIO::ProcessIO; 0667 else 0668 qCCritical(KFOURINLINE_LOG) << "Unknown input device for player 0"; 0669 if (global_demo_mode) 0670 mode = KGameIO::ProcessIO; 0671 setPlayedBy(Yellow, mode); 0672 qCDebug(KFOURINLINE_LOG) << "Played by Yellow=" << mode; 0673 0674 if (Prefs::input1mouse()) 0675 mode = KGameIO::MouseIO; 0676 else if (Prefs::input1key()) 0677 mode = KGameIO::KeyIO; 0678 else if (Prefs::input1ai()) 0679 mode = KGameIO::ProcessIO; 0680 else 0681 qCCritical(KFOURINLINE_LOG) << "Unknown input device for player 1"; 0682 if (global_demo_mode) 0683 mode = KGameIO::ProcessIO; 0684 setPlayedBy(Red, mode); 0685 qCDebug(KFOURINLINE_LOG) << "Played by Red=" << mode; 0686 0687 if (Prefs::startcolourred()) 0688 mStartPlayer.setValue(Red); 0689 else if (Prefs::startcolouryellow()) 0690 mStartPlayer.setValue(Yellow); 0691 else 0692 qCCritical(KFOURINLINE_LOG) << "Unknown start color"; 0693 qCDebug(KFOURINLINE_LOG) << "Setting start player to" << mStartPlayer; 0694 } 0695 0696 // Read config file 0697 void KWin4Doc::readConfig(KConfig *config) 0698 { 0699 qCDebug(KFOURINLINE_LOG) << "++++++++++++++++++++++++++++++++++++ KWin4Doc::ReadConfig"; 0700 loadSettings(); 0701 0702 KConfigGroup ygrp = config->group(QStringLiteral("YellowPlayer")); 0703 getPlayer(Yellow)->readConfig(ygrp); 0704 0705 KConfigGroup rgrp = config->group(QStringLiteral("RedPlayer")); 0706 getPlayer(Red)->readConfig(rgrp); 0707 } 0708 0709 // Write config file 0710 void KWin4Doc::writeConfig(KConfig *config) 0711 { 0712 KConfigGroup ygrp = config->group(QStringLiteral("YellowPlayer")); 0713 getPlayer(Yellow)->writeConfig(ygrp); 0714 0715 KConfigGroup rgrp = config->group(QStringLiteral("RedPlayer")); 0716 getPlayer(Red)->writeConfig(rgrp); 0717 0718 config->sync(); 0719 } 0720 0721 // Returns the current player, resp amzug. 0722 COLOUR KWin4Doc::getCurrentPlayer() 0723 { 0724 return (COLOUR)mAmzug.value(); 0725 } 0726 0727 // Set the current player 0728 void KWin4Doc::setCurrentPlayer(COLOUR no) 0729 { 0730 mAmzug.setValue(no); 0731 } 0732 0733 // Switch the starting player and return the new started 0734 COLOUR KWin4Doc::switchStartPlayer() 0735 { 0736 if (mStartPlayer.value() == Yellow) { 0737 mStartPlayer.setValue(Red); 0738 Prefs::setStartcolouryellow(false); 0739 Prefs::setStartcolourred(true); 0740 qCDebug(KFOURINLINE_LOG) << "Setting startplayer to RED"; 0741 } else { 0742 mStartPlayer.setValue(Yellow); 0743 Prefs::setStartcolouryellow(true); 0744 Prefs::setStartcolourred(false); 0745 qCDebug(KFOURINLINE_LOG) << "Setting startplayer to YELLOW"; 0746 } 0747 Prefs::self()->save(); 0748 0749 return (COLOUR)mStartPlayer.value(); 0750 } 0751 0752 // Retrieve the current move number. 0753 int KWin4Doc::getCurrentMove() 0754 { 0755 return mCurrentMove; 0756 } 0757 0758 // Retrieve the maximum move number before undo 0759 int KWin4Doc::getMaxMove() 0760 { 0761 return mMaxMove; 0762 } 0763 0764 // Retrieve the amount of history moves stored 0765 int KWin4Doc::getHistoryCnt() 0766 { 0767 return mHistoryCnt; 0768 } 0769 0770 // Return the filename of the computer player AI process. 0771 QString KWin4Doc::findProcessName() 0772 { 0773 // Try whether we run from a development source dir 0774 #ifndef NDEBUG 0775 #ifdef SRC_DIR 0776 QString srcname = QStringLiteral(SRC_DIR) + QStringLiteral("/src/kfourinlineproc"); 0777 QFile fsrc(srcname); 0778 if (fsrc.exists()) { 0779 if (global_debug > 1) 0780 qCDebug(KFOURINLINE_LOG) << "Found SRC_DIR process" << srcname; 0781 return srcname; 0782 } 0783 #endif 0784 #endif 0785 0786 // First try a local dir override 0787 QDir dir; 0788 // TODO: This local filename is not found!! 0789 QString filename = dir.path() + QStringLiteral("/kwin4/kfourinlineproc"); 0790 qCDebug(KFOURINLINE_LOG) << "PROC FILENAME=" << filename; 0791 QFile flocal(filename); 0792 if (flocal.exists()) { 0793 if (global_debug > 1) 0794 qCDebug(KFOURINLINE_LOG) << "Found local process" << filename; 0795 return filename; 0796 } 0797 QString path = QStandardPaths::findExecutable(QStringLiteral("kfourinlineproc")); 0798 if (!path.isNull()) { 0799 if (global_debug > 1) 0800 qCDebug(KFOURINLINE_LOG) << "Found system process" << path; 0801 return path; 0802 } 0803 QString empty; 0804 qCCritical(KFOURINLINE_LOG) << "Could not locate the computer player"; 0805 return empty; 0806 } 0807 0808 // Debug: Listen to messages 0809 void KWin4Doc::networkMessageUpdate(int /*id*/, quint32 /*sender*/, quint32 /*recv*/) 0810 { 0811 // qCDebug(KFOURINLINE_LOG) << "MSG: id=" << id << "sender=" << sender << "receiver="<<recv; 0812 } 0813 0814 // Create a KPlayer 0815 KPlayer *KWin4Doc::createPlayer(int /*rtti*/, int io, bool isvirtual) 0816 { 0817 KWin4Player *player = new KWin4Player; 0818 if (!isvirtual) 0819 createIO(player, (KGameIO::IOMode)io); 0820 0821 connect(player, &KWin4Player::signalPropertyChanged, this, &KWin4Doc::playerPropertyChanged); 0822 player->setStatus(mStatus); 0823 return player; 0824 } 0825 0826 // Called when a player input is received from the KGame object 0827 // this is e.g. a mouse event, the AI or the network 0828 bool KWin4Doc::playerInput(QDataStream &msg, KPlayer * /*player*/) 0829 { 0830 qint32 move, pl; 0831 msg >> pl >> move; 0832 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::playerInput: ================ pl=" << pl << " and move=" << move << "===================="; 0833 0834 // Perform move and check for success 0835 if (!doMove(move, pl)) { 0836 // Repeat the same input 0837 QTimer::singleShot(0, this, &KWin4Doc::repeatMove); 0838 } 0839 0840 return false; 0841 } 0842 0843 // Reactivate player in case of a move which could not pe performed. 0844 void KWin4Doc::repeatMove() 0845 { 0846 getPlayer(getCurrentPlayer())->setTurn(true); 0847 } 0848 0849 // Query the IO mode of player og the given color. 0850 KGameIO::IOMode KWin4Doc::playedBy(int col) 0851 { 0852 return mPlayedBy[col]; 0853 } 0854 0855 // Sets the input device mode for the given player color. 0856 void KWin4Doc::setPlayedBy(int col, KGameIO::IOMode io) 0857 { 0858 if (global_debug > 1) 0859 qCDebug(KFOURINLINE_LOG) << " KWin4Doc::setPlayedBy(int " << col << ",KGameIO::IOMode " << io << ")"; 0860 0861 KWin4Player *player = getPlayer((COLOUR)col); 0862 0863 // Modes for the score sprite 0864 player->status()->setPlayedBy((int)io, player->userId()); 0865 0866 if (mPlayedBy[col] != io && !player->isVirtual()) { 0867 bool myTurn = player->myTurn(); 0868 player->setTurn(false); // turn of move 0869 mPlayedBy[col] = io; 0870 player->removeGameIO(); // remove all IO's 0871 createIO(player, io); 0872 player->setTurn(myTurn); // turn on move 0873 } 0874 } 0875 0876 // Get the io values right after a load game as the io the playedby 0877 // is not set there. 0878 void KWin4Doc::recalcIO() 0879 { 0880 mPlayedBy[Yellow] = (KGameIO::IOMode)getPlayer(Yellow)->calcIOValue(); 0881 mPlayedBy[Red] = (KGameIO::IOMode)getPlayer(Red)->calcIOValue(); 0882 } 0883 0884 // Create player input devicea (KGame) 0885 void KWin4Doc::createIO(KPlayer *player, KGameIO::IOMode io) 0886 { 0887 if (!player) 0888 return; 0889 0890 if (global_debug > 1) 0891 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::createIO(KPlayer *player(" << player->userId() << "),KGameIO::IOMode " << io << ") "; 0892 0893 if (io & KGameIO::MouseIO) { 0894 KGameMouseIO *input; 0895 if (global_debug > 1) 0896 qCDebug(KFOURINLINE_LOG) << "Creating MOUSE IO to " << pView; 0897 // We want the player to work over mouse. So please leave the "true" for mouse 0898 // tracking on !!! 0899 input = new KGameMouseIO(pView->viewport(), true); 0900 if (global_debug > 1) 0901 qCDebug(KFOURINLINE_LOG) << "MOUSE IO added"; 0902 // Connect mouse input to a function to process the actual input 0903 connect(input, &KGameMouseIO::signalMouseEvent, pView, &KWin4View::mouseInput); 0904 player->addGameIO(input); 0905 } else if (io & KGameIO::ProcessIO) { 0906 QString file = findProcessName(); 0907 if (global_debug > 1) 0908 qCDebug(KFOURINLINE_LOG) << "Creating PROCESS IO" << file; 0909 0910 KGameProcessIO *input; 0911 // We want a computer player 0912 input = new KGameProcessIO(file); 0913 // Connect computer player to the setTurn 0914 connect(input, &KGameProcessIO::signalPrepareTurn, this, &KWin4Doc::prepareAITurn); 0915 0916 connect(input, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAICommand); 0917 0918 connect(input, &KGameProcessIO::signalReceivedStderr, this, &KWin4Doc::receivedStderr); 0919 player->addGameIO(input); 0920 } else if (io & KGameIO::KeyIO) { 0921 if (global_debug > 1) 0922 qCDebug(KFOURINLINE_LOG) << "Creating KEYBOARD IO"; 0923 // We want the player to work over keyboard 0924 KGameKeyIO *input; 0925 input = new KGameKeyIO(pView->parentWidget()); 0926 // Connect keys input to a function to process the actual input 0927 connect((KGameKeyIO *)input, &KGameKeyIO::signalKeyEvent, pView, &KWin4View::keyInput); 0928 player->addGameIO(input); 0929 } 0930 } 0931 0932 void KWin4Doc::receivedStderr(const QString &s) 0933 { 0934 if (global_debug > 0) 0935 qCDebug(KFOURINLINE_LOG) << "##### AI:" << s; 0936 } 0937 0938 // This slot is called when a computer move should be generated 0939 void KWin4Doc::prepareAITurn(QDataStream &stream, bool b, KGameIO *input, bool *sendit) 0940 { 0941 if (global_debug > 1) 0942 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::prepareAITurn b=" << b; 0943 0944 // Set defaults 0945 *sendit = false; 0946 0947 // Our player 0948 KPlayer *player = input->player(); 0949 if (!player->myTurn()) 0950 return; 0951 if (!b) 0952 return; // only create move on setTurn(true) 0953 0954 qint32 pl; 0955 if (global_debug > 1) 0956 qCDebug(KFOURINLINE_LOG) << "slotPrepareComputerTurn for player id=" << player->id(); 0957 pl = player->userId(); 0958 0959 // Pack the game into the message 0960 prepareGameMessage(stream, pl); 0961 0962 // Do send 0963 *sendit = true; 0964 } 0965 0966 // Sends the current game status to the computer player 0967 // Careful: The data needs to be exactly the same as the computer 0968 // player reading on the other side 0969 void KWin4Doc::prepareGameMessage(QDataStream &stream, qint32 pl) 0970 { 0971 if (global_debug > 1) 0972 qCDebug(KFOURINLINE_LOG) << " sending col=" << pl; 0973 stream << pl; 0974 // This needs to be the same than the computer player reads! 0975 stream << (qint32)getCurrentMove(); 0976 stream << (qint32)getCurrentPlayer(); 0977 stream << (qint32)getPlayerColour(0); 0978 stream << (qint32)getPlayerColour(1); 0979 stream << (qint32)Prefs::level(); 0980 0981 bool learning = Prefs::learning(); 0982 // Allow learning only for one AI 0983 if (mPlayedBy[Yellow] == KGameIO::ProcessIO && mPlayedBy[Red] == KGameIO::ProcessIO && pl == Yellow) 0984 learning = false; 0985 stream << (qint32)learning; 0986 0987 // Where to save the learn cache 0988 QString learnPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("kwin4"); 0989 0990 stream << learnPath; 0991 0992 int i, j; 0993 for (i = 0; i < FIELD_SIZE_Y; ++i) { 0994 for (j = 0; j < FIELD_SIZE_X; ++j) { 0995 qint8 col; 0996 col = getColour(j, i); 0997 stream << col; 0998 } 0999 if (global_debug > 1) 1000 qCDebug(KFOURINLINE_LOG) << getColour(0, i) << " " << getColour(1, i) << " " << getColour(2, i) << " " << getColour(3, i) << " " << getColour(4, i) 1001 << " " << getColour(5, i) << " " << getColour(6, i); 1002 } 1003 stream << (qint32)421256; 1004 } 1005 1006 // The AI send a command, e.g. the game value to us 1007 void KWin4Doc::processAICommand(QDataStream &in, KGameProcessIO *io) 1008 { 1009 qint8 cid; 1010 1011 // Receive command 1012 in >> cid; 1013 switch (cid) { 1014 case 1: // game value 1015 { 1016 AIBoard aiBoard; 1017 qint32 value, moveNo, level; 1018 in >> value >> moveNo >> level >> aiBoard; 1019 if (global_debug > 1) 1020 qCDebug(KFOURINLINE_LOG) << "#### Computer thinks move" << moveNo << "value is" << value; 1021 // Store AI data 1022 mAIValues[moveNo] = value; 1023 setScore(value); 1024 1025 // Check AI mistakes 1026 if (moveNo >= 2) { 1027 long delta = mAIValues[moveNo] - mAIValues[moveNo - 2]; 1028 1029 // Send game to process 1030 QByteArray buffer; 1031 QDataStream outstream(&buffer, QIODevice::WriteOnly); 1032 // Send last board to learn with current value 1033 outstream << aiBoard << value << (qint32)delta << level; 1034 io->sendMessage(outstream, 3, 0, gameId()); 1035 } 1036 } break; 1037 default: 1038 qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAICommand: Unknown id" << cid; 1039 break; 1040 } 1041 } 1042 1043 // This slot is called by the signal of KGame to indicated 1044 // that the network connection is done and a new client is 1045 // connected 1046 // cid is the id of the client connected. if this is equal 1047 // gameId() WE are the client 1048 void KWin4Doc::clientConnected(quint32 cid, KGame * /* me */) 1049 { 1050 if (global_debug > 1) 1051 qCDebug(KFOURINLINE_LOG) << "void KWin4Doc::clientConnected id=" << cid << "we=" << gameId() << "we admin=" << isAdmin() << "master)" << isMaster(); 1052 1053 if (playerList()->count() != 2) { 1054 qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: We do not have two players...Trying to disconnect!"; 1055 disconnect(); 1056 return; 1057 } 1058 1059 // Get the two players - more are not possible 1060 KWin4Player *p1 = (KWin4Player *)playerList()->at(0); 1061 KWin4Player *p2 = (KWin4Player *)playerList()->at(1); 1062 if (!p1->isVirtual()) { 1063 Q_EMIT signalChatChanged(p1); 1064 if (global_debug > 1) 1065 qCDebug(KFOURINLINE_LOG) << "CHAT to player 0"; 1066 } else { 1067 Q_EMIT signalChatChanged(p2); 1068 if (global_debug > 1) 1069 qCDebug(KFOURINLINE_LOG) << "CHAT to player 1"; 1070 } 1071 1072 // Now check whose turn it is. The Admin will rule this 1073 if (isAdmin()) { 1074 if (global_debug > 1) 1075 qCDebug(KFOURINLINE_LOG) << "WE are ADMIN == COOL ! "; 1076 // p1 is local 1077 if (!p1->isVirtual()) { 1078 if (global_debug > 1) 1079 qCDebug(KFOURINLINE_LOG) << "p1 id=" << p1->userId() << "is local turn=" << p1->myTurn(); 1080 // Exclusive setting of the turn 1081 p1->setTurn(p1->myTurn(), true); 1082 p2->setTurn(!p1->myTurn(), true); 1083 } else if (!p2->isVirtual()) { 1084 if (global_debug > 1) 1085 qCDebug(KFOURINLINE_LOG) << "p2 id=" << p2->userId() << "is local turn=" << p2->myTurn(); 1086 // Exclusive setting of the turn 1087 p2->setTurn(p2->myTurn(), true); 1088 p1->setTurn(!p2->myTurn(), true); 1089 } 1090 } 1091 } 1092 1093 // Get the KPlayer from the color by searching all players 1094 // users id's 1095 KWin4Player *KWin4Doc::getPlayer(COLOUR col) 1096 { 1097 for (KGamePlayerList::const_iterator it = playerList()->constBegin(); it != playerList()->constEnd(); ++it) { 1098 if ((*it)->userId() == col) 1099 return (KWin4Player *)(*it); 1100 } 1101 qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: Cannot find player with colour" << col << ". CRASH imminent"; 1102 return nullptr; 1103 } 1104 1105 // We create a process which calculates a computer move which is shown as hint to the player. 1106 void KWin4Doc::calculateHint() 1107 { 1108 // We allocate the hint process only if it is needed 1109 if (!mHintProcess) { 1110 QString file = findProcessName(); 1111 if (global_debug > 1) 1112 qCDebug(KFOURINLINE_LOG) << "Creating HINT PROCESS"; 1113 1114 // We want a computer player 1115 mHintProcess = new KGameProcessIO(file); 1116 1117 connect(mHintProcess, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAIHintCommand); 1118 } 1119 1120 // Send game to process 1121 qint32 pl; 1122 QByteArray buffer; 1123 QDataStream stream(&buffer, QIODevice::WriteOnly); 1124 pl = getCurrentPlayer(); 1125 prepareGameMessage(stream, pl); 1126 mHintProcess->sendMessage(stream, 2, 0, gameId()); 1127 } 1128 1129 // The compute process sent a hint which we show in the game board. 1130 void KWin4Doc::processAIHintCommand(QDataStream &in, KGameProcessIO * /*io*/) 1131 { 1132 qint8 cid; 1133 // Read command 1134 in >> cid; 1135 switch (cid) { 1136 case 2: // Hint 1137 { 1138 qint32 pl; 1139 qint32 move; 1140 qint32 value; 1141 // Read parameters of command 1142 in >> pl >> move >> value; 1143 if (global_debug > 1) 1144 qCDebug(KFOURINLINE_LOG) << "#### Computer thinks pl=" << pl << "move =" << move; 1145 if (global_debug > 1) 1146 qCDebug(KFOURINLINE_LOG) << "#### Computer thinks hint is" << move << "and value is" << value; 1147 1148 // Display hint 1149 int x = move; 1150 int y = mFieldFilled.at(x); 1151 pView->displayHint(x, y); 1152 } break; 1153 default: 1154 qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAIHintCommand: Unknown id" << cid; 1155 break; 1156 } 1157 } 1158 1159 // Called when a player property has changed. We check whether the name 1160 // changed and then update the score widget 1161 // We should maybe do this for the other properties too to update 1162 // the status widget...I am not sure here...we'll see 1163 void KWin4Doc::playerPropertyChanged(KGamePropertyBase *prop, KPlayer *player) 1164 { 1165 if (!pView) 1166 return; 1167 1168 // Check for name changes 1169 if (prop->id() == KGamePropertyBase::IdName) { 1170 if (global_debug > 1) 1171 qCDebug(KFOURINLINE_LOG) << "Player name id=" << player->userId() << "changed to" << player->name(); 1172 mStatus->setPlayerName(player->name(), player->userId()); 1173 } 1174 } 1175 1176 // Called by KGame when a game property has changed. 1177 void KWin4Doc::gamePropertyChanged(KGamePropertyBase *prop, KGame * /* me */) 1178 { 1179 if (!pView) 1180 return; 1181 1182 // Move number 1183 if (prop->id() == mCurrentMove.id()) { 1184 // TODO pView->scoreWidget()->setMove(mCurrentMove); 1185 } 1186 1187 // Computer AI value 1188 else if (prop->id() == mScore.id()) { 1189 int sc = mScore / 10000; 1190 if (sc == 0 && mScore.value() > 0) 1191 sc = 1; 1192 else if (sc == 0 && mScore.value() < 0) 1193 sc = -1; 1194 // TODO pView->scoreWidget()->setChance(sc); 1195 } 1196 1197 // Whose turn is it 1198 else if (prop->id() == mAmzug.id()) { 1199 if (global_debug > 1) 1200 qCDebug(KFOURINLINE_LOG) << "Amzug changed to" << mAmzug.value(); 1201 mStatus->setTurn(mAmzug); 1202 } 1203 1204 // The game status 1205 else if (prop->id() == KGamePropertyBase::IdGameStatus) { 1206 if (gameStatus() == Abort) { 1207 if (global_debug > 1) 1208 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game abort +++"; 1209 Q_EMIT signalGameOver(2, getPlayer(getCurrentPlayer()), this); // 2 indicates Abort 1210 } else if (gameStatus() == Run) { 1211 if (global_debug > 1) 1212 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run +++"; 1213 if (playerList()->count() == 2) { 1214 activateCurrentPlayer(); // Set the current player to play 1215 Q_EMIT signalGameRun(); 1216 } 1217 if (global_debug > 1) 1218 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run done +++"; 1219 } else if (gameStatus() == Init) { 1220 if (global_debug > 1) 1221 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game INIT +++"; 1222 resetGame(true); 1223 } else if (gameStatus() == End) { 1224 if (global_debug > 1) 1225 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game END +++"; 1226 } else { 1227 if (global_debug > 1) 1228 qCDebug(KFOURINLINE_LOG) << "PropertyChanged::other status signal +++"; 1229 } 1230 } 1231 } 1232 1233 // This is an overwritten function of KGame which is called 1234 // when a game is loaded. This can either be via a network 1235 // connect or via a real load from file 1236 bool KWin4Doc::loadgame(QDataStream &stream, bool network, bool reset) 1237 { 1238 if (global_debug > 1) 1239 qCDebug(KFOURINLINE_LOG) << "loadgame() network=" << network << "reset=" << reset; 1240 if (!network) 1241 setGameStatus(End); 1242 1243 // Clear out the old game 1244 if (global_debug > 1) 1245 qCDebug(KFOURINLINE_LOG) << "loadgame wants to reset the game"; 1246 resetGame(true); 1247 1248 // load the new game 1249 bool res = KGame::loadgame(stream, network, reset); 1250 if (global_debug > 1) 1251 qCDebug(KFOURINLINE_LOG) << "amzug loaded to =" << mAmzug.value(); 1252 1253 // Replay the game be undoing and redoing 1254 if (global_debug > 1) 1255 qCDebug(KFOURINLINE_LOG) << "REDRAW GAME using undo/redo"; 1256 if (global_debug > 1) 1257 qCDebug(KFOURINLINE_LOG) << "history cnt=" << mHistoryCnt.value(); 1258 if (global_debug > 1) 1259 qCDebug(KFOURINLINE_LOG) << "amzug =" << mAmzug.value(); 1260 int cnt = 0; 1261 while (undoMove()) { 1262 ++cnt; 1263 if (global_debug > 1) 1264 qCDebug(KFOURINLINE_LOG) << "Undoing move " << cnt; 1265 } 1266 if (global_debug > 1) 1267 qCDebug(KFOURINLINE_LOG) << "amzug =" << mAmzug.value(); 1268 while (cnt > 0) { 1269 redoMove(); 1270 --cnt; 1271 if (global_debug > 1) 1272 qCDebug(KFOURINLINE_LOG) << "Redoing move " << cnt; 1273 } 1274 if (global_debug > 1) 1275 qCDebug(KFOURINLINE_LOG) << "amzug =" << mAmzug.value(); 1276 1277 // Set the input devices 1278 recalcIO(); 1279 // And set the right player to turn 1280 activateCurrentPlayer(); 1281 1282 if (global_debug > 1) 1283 qCDebug(KFOURINLINE_LOG) << "loadgame done +++"; 1284 return res; 1285 } 1286 1287 // This is also an overwritten function of KGame. It is 1288 // Called in the game negotiation upon connect. Here 1289 // the games have to determine what player is remote and 1290 // what is local 1291 // This function is only called in the Admin. 1292 void KWin4Doc::newPlayersJoin(KGamePlayerList * /*oldList*/, KGamePlayerList *newList, QList<int> &inactivate) 1293 { 1294 if (global_debug > 1) 1295 qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: START"; 1296 1297 KWin4Player *yellow = getPlayer(Yellow); 1298 KWin4Player *red = getPlayer(Red); 1299 // Take the master player with the higher priority. Priority is set 1300 // by the network dialog 1301 if (yellow->networkPriority() > red->networkPriority()) { 1302 // Deactivate the lower one 1303 inactivate.append(red->id()); 1304 if (global_debug > 1) 1305 qCDebug(KFOURINLINE_LOG) << "ADMIN keeps yellow and kicks red=" << red->id() << " userId/col=" << red->userId(); 1306 // loop all client players and deactivate the one which have the color 1307 // yellow 1308 for (KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it) { 1309 KPlayer *player = *it; 1310 if (player->userId() == yellow->userId()) { 1311 inactivate.append(player->id()); 1312 if (global_debug > 1) 1313 qCDebug(KFOURINLINE_LOG) << "Deactivate C1" << player->id() << " col=" << player->userId(); 1314 } 1315 } 1316 } else { 1317 // Deactivate the lower one 1318 inactivate.append(yellow->id()); 1319 if (global_debug > 1) 1320 qCDebug(KFOURINLINE_LOG) << "ADMIN keeps red and kicks yellow=" << yellow->id() << " userId/col=" << yellow->userId(); 1321 // loop all client players and deactivate the one which have the color 1322 // red 1323 for (KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it) { 1324 KPlayer *player = *it; 1325 if (player->userId() == red->userId()) { 1326 inactivate.append(player->id()); 1327 if (global_debug > 1) 1328 qCDebug(KFOURINLINE_LOG) << "Deactivate C2" << player->id() << " col=" << player->userId(); 1329 } 1330 } 1331 } 1332 if (global_debug > 1) 1333 qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: DONE"; 1334 } 1335 1336 #include "moc_kwin4doc.cpp"