1 /*
2 This file is part of the KDE games kwin4 program
3 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 /** Note: The AI engine of kwin4 does on purpose not implement a perfect
9 * Connect-4(tm) engine but tries to play more human as playing against
10 * a perfect engine is only frustrating. Connect four is proven to
11 * always win for the first player with perfect play.
12 * see e.g. the Velena AI Engine http://web.archive.org/web/20180219021151/http://www.ce.unipr.it/~gbe/velena.html
13 */
14
15 #include "kwin4doc.h"
16
17 // own
18 #include "kfourinline_debug.h"
19 #include "kwin4view.h"
20 #include "scoresprite.h"
21 #include "prefs.h"
22 #include "score.h"
23 #include "ui_statuswidget.h"
24 #include "config-src.h"
25 // KF
26 #include <KLocalizedString>
27 // Qt
28 #include <QDir>
29 #include <QStandardPaths>
30 #include <QTimer>
31
32
33 #define FIELD_SIZE_X 7
34 #define FIELD_SIZE_Y 6
35
36 // Constructor
KWin4Doc(QWidget * parent)37 KWin4Doc::KWin4Doc(QWidget *parent) : KGame(1234,parent), pView(), mHintProcess()
38 {
39 mStatus = new Score(parent);
40
41 connect(this, &KWin4Doc::signalPropertyChanged, this, &KWin4Doc::gamePropertyChanged);
42
43 dataHandler()->Debug();
44 //qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy();
45 setPolicy(KGame::PolicyDirty,true);
46
47 //qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy();
48
49 // Game design
50 setMaxPlayers(2);
51 setMinPlayers(2);
52
53 // Game initialization
54 mField.resize(42);
55
56 // ****************************************
57 // NOTE: Do not i18n the strings here. They
58 // are for debugging only
59 // ****************************************
60
61 // The field array needs not be updated as any move will change it
62 // Careful only in new resetGame! Maybe unlocal it there!
63 // mField.setPolicy(KGamePropertyBase::PolicyLocal);
64 mField.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mField"));
65
66 mFieldFilled.resize(7);
67 mHistory.resize(43);
68 mHistory.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mHistory"));
69
70 mAmzug.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mAmzug"));
71 mCurrentMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mCurrentMove"));
72 mMaxMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mMaxMove"));
73 mFieldFilled.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mFieldFilled"));
74 mHistoryCnt.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mHistoryCnt"));
75 mLastColumn.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastColumn"));
76 mLastHint.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastHint"));
77 mLastColour.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastColour"));
78 mScore.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mScore"));
79
80 // game startup parameter
81 mStartPlayer=Yellow;
82 mStartPlayer.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QStringLiteral("mStartPlayer"));
83 setCurrentPlayer((COLOUR)mStartPlayer.value());
84 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amZug policy=" << mAmzug.policy();
85
86 mPlayedBy[Yellow] = KGameIO::MouseIO;
87 mPlayedBy[Red] = KGameIO::MouseIO;
88
89
90 // AI support
91 mAIValues.resize(42);
92
93 // last in init
94 resetGame(false);
95
96 setGameStatus(Intro);
97
98 // Listen to network
99 connect(this, &KWin4Doc::signalMessageUpdate, this, &KWin4Doc::networkMessageUpdate);
100 connect(this, &KWin4Doc::signalClientJoinedGame, this, &KWin4Doc::clientConnected);
101
102 // Change global KGame policy
103 //dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false);
104 dataHandler()->Debug();
105 }
106
107
108 // Destructor
~KWin4Doc()109 KWin4Doc::~KWin4Doc()
110 {
111 qCDebug(KFOURINLINE_LOG) << "~KWin4Doc()";
112 delete mHintProcess;
113 delete mStatus;
114 qCDebug(KFOURINLINE_LOG) << "~KWin4Doc() done";
115 }
116
117
118 // Player initialization
initPlayers()119 void KWin4Doc::initPlayers()
120 {
121 // Create yellow
122 KWin4Player* yellow = (KWin4Player*)createPlayer(1, mPlayedBy[Yellow], false);
123 yellow->setUserId(Yellow);
124 yellow->setName(Prefs::name1());
125 addPlayer(yellow);
126 setPlayedBy(Yellow,mPlayedBy[Yellow]);
127
128 // Create Red
129 KWin4Player* red = (KWin4Player*)createPlayer(1, mPlayedBy[Red], false);
130 red->setUserId(Red);
131 red->setName(Prefs::name1());
132 addPlayer(red);
133 setPlayedBy(Red,mPlayedBy[Red]);
134 }
135
136
137 // Set the view to the doc
setView(KWin4View * view)138 void KWin4Doc::setView(KWin4View *view)
139 {
140 pView=view;
141 connect(pView, &KWin4View::signalMoveDone, this, &KWin4Doc::moveDone);
142 }
143
144
145 // Returns colour on the game board
getColour(int x,int y)146 COLOUR KWin4Doc::getColour(int x,int y)
147 {
148 return (COLOUR)mField.at(x+y*FIELD_SIZE_X);
149 }
150
151
152 // Set the colour on the game board
setColour(int x,int y,COLOUR c)153 void KWin4Doc::setColour(int x,int y,COLOUR c)
154 {
155 if (x<0 || x>=FIELD_SIZE_X || y<0 || y>=FIELD_SIZE_Y)
156 {
157 qCCritical(KFOURINLINE_LOG) << "ERROR: setColour on wrong position" << x << " " << y;
158 return ;
159 }
160 mField.setAt(x+y*FIELD_SIZE_X,c);
161 }
162
163
164 // Reset the whole game (and the view)
resetGame(bool initview)165 void KWin4Doc::resetGame(bool initview)
166 {
167 // Reset field
168 for (int x=0;x<FIELD_SIZE_X;++x)
169 {
170 for (int y=FIELD_SIZE_Y-1;y>=0;--y)
171 {
172 setColour(x,y,Nobody);
173 }
174 }
175 mFieldFilled.fill(0);
176
177 // Reset game vars
178 mHistoryCnt=0;
179 mCurrentMove=0;
180 mMaxMove=0;
181 mLastColumn=-1;
182 mLastColour=Nobody;
183 setScore(0);
184 mLastHint=-1;
185
186 // Reset the view
187 if (initview)
188 {
189 pView->initGame(mStatus);
190 }
191
192 // Who starts this game
193 setCurrentPlayer((COLOUR)mStartPlayer.value());
194 }
195
196
197 // Set current player to setTurn true
activateCurrentPlayer()198 void KWin4Doc::activateCurrentPlayer()
199 {
200 if (global_debug>1)
201 qCDebug(KFOURINLINE_LOG) << "Setting the current player to turn";
202 getPlayer(getCurrentPlayer())->setTurn(true,true);
203 }
204
205
206 // End a game. Update statistic and forward end game to view.
endGame(TABLE mode)207 void KWin4Doc::endGame(TABLE mode)
208 {
209 setGameStatus(End);
210 // TODO pView->clearError();
211 pView->endGame();
212
213 // Increase game statistics
214 KWin4Player *yellow=getPlayer(Yellow);
215 KWin4Player *red=getPlayer(Red);
216 switch(mode)
217 {
218 case TWin: yellow->incWin();
219 red->incLost();
220 break;
221 case TLost: yellow->incLost();
222 red->incWin();
223 break;
224 case TRemis: yellow->incRemis();
225 red->incRemis();
226 break;
227 default:
228 // Only break if moves have been made
229 if (mMaxMove>0)
230 {
231 yellow->incBrk();
232 red->incBrk();
233 }
234 break;
235 }
236
237 }
238
239
240 // Indication that a move has been visually done
moveDone(int)241 void KWin4Doc::moveDone(int /*mode*/ )
242 {
243 if (playerCount()>1)
244 {
245 playerInputFinished(getPlayer(getCurrentPlayer()));
246 }
247
248 // TODO pView->clearError();
249 }
250
251
252 // Calculate the next players to turn. Here the players just swap.
nextPlayer(KPlayer * last,bool)253 KPlayer* KWin4Doc::nextPlayer(KPlayer* last, bool /*exclusive*/)
254 {
255 if (global_debug>1)
256 qCDebug(KFOURINLINE_LOG) << "nextPlayer last="<<last->id() << "admin=" << isAdmin();
257
258 // Should be enough if the admin sets the turn
259 if (last->userId()==Yellow)
260 setCurrentPlayer(Red);
261 else
262 setCurrentPlayer(Yellow);
263 if (global_debug>1)
264 qCDebug(KFOURINLINE_LOG) <<" Current set to "<<getCurrentPlayer();
265 if (isAdmin())
266 getPlayer(getCurrentPlayer())->setTurn(true,true);
267 Q_EMIT signalNextPlayer(int(getCurrentPlayer()));
268 return getPlayer(getCurrentPlayer());
269 }
270
271
272 // Performs a game move on the given x position. Just calls makeMove()
273 // and transforms the return value so that true indicates a valid move.
doMove(int x,int id)274 bool KWin4Doc::doMove(int x,int id)
275 {
276 if (global_debug>1)
277 qCDebug(KFOURINLINE_LOG) <<" KWin4Doc::Move pos="<<x<<" id="<<id<<" ";
278
279 return (makeMove(x,0) == GNormal);
280 }
281
282
283 // Make a game move on the given position. Display it in the view.
284 // mode=0 normal move, =1: redo move
makeMove(int x,int mode)285 MOVESTATUS KWin4Doc::makeMove(int x, int mode)
286 {
287 if (x<0 || x>=FIELD_SIZE_X)
288 {
289 qCDebug(KFOURINLINE_LOG) << "ERROR: makeMove auf falsche Position" << x;
290 return GNotAllowed;
291 }
292
293 int y=mFieldFilled.at(x);
294
295 if (y>=FIELD_SIZE_Y)
296 {
297 return GIllMove; // no space left in column
298 }
299
300 if (mLastHint>=0)
301 {
302 int hy;
303 hy=mFieldFilled.at(mLastHint);
304 setColour(mLastHint,hy,Nobody);
305 mLastHint=-1;
306 }
307 if (mode==Tip)
308 {
309 mLastHint=x;
310 setColour(x,y,Tip);
311 return GTip ; // no real move
312 }
313
314 mFieldFilled.setAt(x,mFieldFilled.at(x)+1);
315 setColour(x,y,getCurrentPlayer());
316
317 mHistory.setAt(getHistoryCnt(),x);
318 mHistoryCnt=mHistoryCnt.value()+1;
319
320 mLastColour=getCurrentPlayer();
321 //if (getCurrentPlayer()==Yellow) setCurrentPlayer(Red);
322 //else setCurrentPlayer(Yellow);
323
324 mCurrentMove=mCurrentMove.value()+1;
325
326 // only if a real move isdone the maxmove is raised
327 if (mode==0) mMaxMove=mCurrentMove.value();
328 mLastColumn=x;
329
330 // Show graphics
331 pView->displayMove(x, y, mLastColour, x, mLastColour, mCurrentMove-1 , mode==1?false:true);
332
333 return GNormal;
334 }
335
336
337 // Undo a move.
undoMove()338 bool KWin4Doc::undoMove()
339 {
340 if (getHistoryCnt()<1) return false;
341
342 if (mLastHint>=0)
343 {
344 int hy;
345 hy=mFieldFilled.at(mLastHint);
346 setColour(mLastHint,hy,Nobody);
347 mLastHint=-1;
348 }
349 // qCDebug(KFOURINLINE_LOG) << "Undo no="<<mHistoryCnt.value();
350 mHistoryCnt=mHistoryCnt.value()-1;
351 int x=mHistory.at(getHistoryCnt());
352 mFieldFilled.setAt(x,mFieldFilled.at(x)-1);
353 int y=mFieldFilled.at(x);
354 // qCDebug(KFOURINLINE_LOG) << "Undo x="<<x << "y=" <<y;
355 setColour(x,y,Nobody);
356 // We have to remove the piece as well...
357
358 mLastColour=getCurrentPlayer();
359 if (getCurrentPlayer()==Yellow) setCurrentPlayer(Red);
360 else setCurrentPlayer(Yellow);
361 mCurrentMove=mCurrentMove.value()-1;
362
363 // Display move and arrow history
364 if (getHistoryCnt()>0)
365 {
366 pView->displayMove(x, y, Nobody, mHistory.at(getHistoryCnt()-1), mLastColour.value(), mCurrentMove, false);
367 }
368 else
369 {
370 pView->displayMove(x, y, Nobody, -1, Nobody, mCurrentMove, false);
371 }
372
373 if (getHistoryCnt()>0)
374 mLastColumn=mHistory.at(getHistoryCnt()-1);
375 else
376 mLastColumn=-1;
377
378 setScore(0);
379
380 return true;
381 }
382
383
384 // Redo a move
redoMove()385 bool KWin4Doc::redoMove()
386 {
387 if (getHistoryCnt()>=mMaxMove) return false;
388
389 int x=mHistory.at(getHistoryCnt());
390 //qCDebug(KFOURINLINE_LOG) << "Redo x=" << x;
391 makeMove(x,1);
392 if (getCurrentPlayer()==Yellow)
393 setCurrentPlayer(Red);
394 else
395 setCurrentPlayer(Yellow);
396 setScore(0);
397 return true;
398 }
399
400
401 // Set the name of the player of the given color
setName(COLOUR col,const QString & n)402 void KWin4Doc::setName(COLOUR col, const QString& n)
403 {
404 getPlayer(col)->setName(n);
405 }
406
407
408 // Retrieve the name of the player of the given color
getName(COLOUR col)409 QString KWin4Doc::getName(COLOUR col)
410 {
411 return getPlayer(col)->name();
412 }
413
414
415 // Returns the all time statistics for player of given color
416 // The mode determines what statistics to access.
getStatistic(COLOUR col,TABLE mode)417 int KWin4Doc::getStatistic(COLOUR col, TABLE mode)
418 {
419 KWin4Player *player=getPlayer(col);
420 switch(mode)
421 {
422 case TWin: return player->win();
423 break;
424 case TRemis: return player->remis();
425 break;
426 case TLost: return player->lost();
427 break;
428 case TBrk: return player->brk();
429 break;
430 case TSum: return (player->win()+player->remis()+player->lost());
431 default:
432 break;
433 }
434 return 0;
435 }
436
437
438 // Retrieve the color of the i-th player. Player 0 is the start
439 // player and player 1 the follow up player.
getPlayerColour(int player)440 COLOUR KWin4Doc::getPlayerColour(int player){
441 if (player==0)
442 return (COLOUR)mStartPlayer.value();
443
444 if (mStartPlayer.value()==Yellow)
445 return Red;
446 else
447 return Yellow;
448 }
449
450
451 // Check whether the current game has a game over situation
452 // return -1: remis, 1:won, 0: continue
checkGameOver(KPlayer * p)453 int KWin4Doc::checkGameOver(KPlayer* p)
454 {
455 if (global_debug>1)
456 qCDebug(KFOURINLINE_LOG) <<"KWin4Doc::checkGameOver::"<<p->userId();
457 return checkGameOver(mLastColumn ,(COLOUR)(mLastColour.value()));
458 }
459
460
461 // Check whether the current game has a game over situation
462 // return -1: remis, 1:won, 0: continue
checkGameOver(int x,COLOUR col)463 int KWin4Doc::checkGameOver(int x, COLOUR col)
464 {
465 int y,i;
466 COLOUR c;
467 int star=1;
468 COLOUR winc=Nobody;
469
470 // Check vertical up
471 int flag=0;
472 for (i=0;i<4;++i)
473 {
474 y=mFieldFilled.at(x)-1-i;
475 if (y>=0)
476 {
477 c=getColour(x,y);
478 if (c==col) ++flag;
479 }
480 }
481 if (flag>=4 )
482 {
483 // Store win fields
484 for (i=0;i<4;++i)
485 {
486 y=mFieldFilled.at(x)-1-i;
487 pView->displayStar(x,y,star++);
488 winc=getColour(x,y);
489 }
490 return 1;
491 }
492 else if (flag>=4) return 1;
493
494 int xx;
495 // Check horizontal to the right
496 y=mFieldFilled.at(x)-1;
497 flag=0;
498 for (i=-3;i<=3 && flag<4;++i)
499 {
500 xx=x+i;
501 if (xx>=0 && xx<FIELD_SIZE_X)
502 {
503 c=getColour(xx,y);
504 if (c==col) ++flag;
505 else flag=0;
506 }
507 }
508 if (flag>=4 )
509 {
510 // Store win fields
511 y=mFieldFilled.at(x)-1;
512 winc=getColour(x,y);
513 int cnt=0;
514 for (i=0;i<4;++i)
515 {
516 xx=x+i;
517 if (xx>=0 && xx<FIELD_SIZE_X)
518 {
519 if (getColour(xx,y)!=winc) break;
520 pView->displayStar(xx,y,star++);
521 ++cnt;
522 }
523 else break;
524 }
525 for (i=-1;i>-4 && cnt<4;--i)
526 {
527 xx=x+i;
528 if (xx>=0 && xx<FIELD_SIZE_X)
529 {
530 if (getColour(xx,y)!=winc) break;
531 pView->displayStar(xx,y,star++);
532 ++cnt;
533 }
534 else break;
535 }
536 return 1;
537 }
538 else if (flag>=4) return 1;
539
540
541 // Check dy+
542 flag=0;
543 for (i=-3;i<=3 && flag<4;++i)
544 {
545 xx=x+i;
546 if (xx>=0 && xx<FIELD_SIZE_X)
547 {
548 y=mFieldFilled.at(x)-1-i;
549 if (y>=0 && y<FIELD_SIZE_Y)
550 {
551 c=getColour(xx,y);
552 if (c==col) ++flag;
553 else flag=0;
554 }
555 }
556 }
557 if (flag>=4 )
558 {
559 // Store win fields
560 y=mFieldFilled.at(x)-1;
561 winc=getColour(x,y);
562 int cnt=0;
563 for (i=0;i<4;++i)
564 {
565 xx=x+i;
566 if (xx>=0 && xx<FIELD_SIZE_X)
567 {
568 y=mFieldFilled.at(x)-1-i;
569 if (y<0) break;
570 if (getColour(xx,y)!=winc) break;
571 pView->displayStar(xx,y,star++);
572 ++cnt;
573 }
574 else break;
575 }
576 for (i=-1;i>-4 && cnt<4;--i)
577 {
578 xx=x+i;
579 if (xx>=0 && xx<FIELD_SIZE_X)
580 {
581 y=mFieldFilled.at(x)-1-i;
582 if (y>=FIELD_SIZE_Y) break;
583 if (getColour(xx,y)!=winc) break;
584 pView->displayStar(xx,y,star++);
585 ++cnt;
586 }
587 else break;
588 }
589 return 1;
590 }
591 else if (flag>=4) return 1;
592
593
594 // Check dy-
595 flag=0;
596 for (i=-3;i<=3 && flag<4;++i)
597 {
598 xx=x+i;
599 if (xx>=0 && xx<FIELD_SIZE_X)
600 {
601 y=mFieldFilled.at(x)-1+i;
602 if (y>=0 && y<FIELD_SIZE_Y)
603 {
604 c=getColour(xx,y);
605 if (c==col) ++flag;
606 else flag=0;
607 }
608 }
609 }
610 if (flag>=4 )
611 {
612 // Store win fields
613 y=mFieldFilled.at(x)-1;
614 winc=getColour(x,y);
615 int cnt=0;
616 for (i=0;i<4;++i)
617 {
618 xx=x+i;
619 if (xx>=0 && xx<FIELD_SIZE_X)
620 {
621 y=mFieldFilled.at(x)-1+i;
622 if (y>=FIELD_SIZE_Y) break;
623 if (getColour(xx,y)!=winc) break;
624 pView->displayStar(xx,y,star++);
625 ++cnt;
626 }
627 else break;
628 }
629 for (i=-1;i>-4 && cnt<4;--i)
630 {
631 xx=x+i;
632 if (xx>=0 && xx<FIELD_SIZE_X)
633 {
634 y=mFieldFilled.at(x)-1+i;
635 if (y<0) break;
636 if (getColour(xx,y)!=winc) break;
637 pView->displayStar(xx,y,star++);
638 ++cnt;
639 }
640 else break;
641 }
642 return 1;
643 }
644 else if (flag>=4) return 1;
645
646 if (mCurrentMove>=42) return -1;
647
648 return 0;
649 }
650
651
652 // Reset all the player stats
resetStatistic()653 void KWin4Doc::resetStatistic()
654 {
655 getPlayer(Yellow)->resetStats();
656 getPlayer(Red)->resetStats();
657 }
658
659
660 // Set computer AI score value
setScore(long value)661 void KWin4Doc::setScore(long value)
662 {
663 mScore.setValue(value);
664 }
665
666
667 // Load settings from Prefs
loadSettings()668 void KWin4Doc::loadSettings()
669 {
670 qCDebug(KFOURINLINE_LOG) << "++++ KWin4Doc::loadSettings() ";
671 qCDebug(KFOURINLINE_LOG) << "Level:" << Prefs::level();
672 qCDebug(KFOURINLINE_LOG) << "Name:" << Prefs::name1();
673 qCDebug(KFOURINLINE_LOG) << "Name2:" << Prefs::name2();
674 qCDebug(KFOURINLINE_LOG) << "input0mouse:" << Prefs::input0mouse();
675 qCDebug(KFOURINLINE_LOG) << "input0key:" << Prefs::input0key();
676 qCDebug(KFOURINLINE_LOG) << "input0ai:" << Prefs::input0ai();
677 qCDebug(KFOURINLINE_LOG) << "input1mouse:" << Prefs::input1mouse();
678 qCDebug(KFOURINLINE_LOG) << "input1key:" << Prefs::input1key();
679 qCDebug(KFOURINLINE_LOG) << "input1ai:" << Prefs::input1ai();
680 qCDebug(KFOURINLINE_LOG) << "start red:" << Prefs::startcolourred();
681 qCDebug(KFOURINLINE_LOG) << "start yellow" << Prefs::startcolouryellow();
682 qCDebug(KFOURINLINE_LOG) << "Learning " << Prefs::learning();
683 qCDebug(KFOURINLINE_LOG) << "Lock " << Prefs::ailock();
684
685
686 // Store level for score sprite display
687 mStatus->setLevel(Prefs::level(), 0);
688 mStatus->setLevel(Prefs::level(), 1);
689
690 setName(Yellow, Prefs::name1());
691 setName(Red, Prefs::name2());
692
693 KGameIO::IOMode mode = KGameIO::MouseIO;
694
695 if(Prefs::input0mouse()) mode = KGameIO::MouseIO;
696 else if(Prefs::input0key()) mode = KGameIO::KeyIO;
697 else if(Prefs::input0ai()) mode = KGameIO::ProcessIO;
698 else qCCritical(KFOURINLINE_LOG) << "Unknown input device for player 0";
699 if (global_demo_mode) mode = KGameIO::ProcessIO;
700 setPlayedBy(Yellow, mode);
701 qCDebug(KFOURINLINE_LOG) << "Played by Yellow="<<mode;
702
703 if(Prefs::input1mouse()) mode = KGameIO::MouseIO;
704 else if(Prefs::input1key()) mode = KGameIO::KeyIO;
705 else if(Prefs::input1ai()) mode = KGameIO::ProcessIO;
706 else qCCritical(KFOURINLINE_LOG) << "Unknown input device for player 1";
707 if (global_demo_mode) mode = KGameIO::ProcessIO;
708 setPlayedBy(Red, mode);
709 qCDebug(KFOURINLINE_LOG) << "Played by Red="<<mode;
710
711 if (Prefs::startcolourred()) mStartPlayer.setValue(Red);
712 else if (Prefs::startcolouryellow()) mStartPlayer.setValue(Yellow);
713 else qCCritical(KFOURINLINE_LOG) << "Unknown start color";
714 qCDebug(KFOURINLINE_LOG) << "Setting start player to" << mStartPlayer;
715
716
717 }
718
719
720 // Read config file
readConfig(KConfig * config)721 void KWin4Doc::readConfig(KConfig *config)
722 {
723 qCDebug(KFOURINLINE_LOG) << "++++++++++++++++++++++++++++++++++++ KWin4Doc::ReadConfig";
724 loadSettings();
725
726 KConfigGroup ygrp = config->group("YellowPlayer");
727 getPlayer(Yellow)->readConfig(ygrp);
728
729 KConfigGroup rgrp = config->group("RedPlayer");
730 getPlayer(Red)->readConfig(rgrp);
731 }
732
733
734 // Write config file
writeConfig(KConfig * config)735 void KWin4Doc::writeConfig(KConfig *config)
736 {
737 KConfigGroup ygrp = config->group("YellowPlayer");
738 getPlayer(Yellow)->writeConfig(ygrp);
739
740 KConfigGroup rgrp = config->group("RedPlayer");
741 getPlayer(Red)->writeConfig(rgrp);
742
743 config->sync();
744 }
745
746
747 // Returns the current player, resp amzug.
getCurrentPlayer()748 COLOUR KWin4Doc::getCurrentPlayer()
749 {
750 return (COLOUR)mAmzug.value();
751 }
752
753
754 // Set the current player
setCurrentPlayer(COLOUR no)755 void KWin4Doc::setCurrentPlayer(COLOUR no)
756 {
757 mAmzug.setValue(no);
758 }
759
760
761 // Switch the starting player and return the new started
switchStartPlayer()762 COLOUR KWin4Doc::switchStartPlayer()
763 {
764 if (mStartPlayer.value()==Yellow)
765 {
766 mStartPlayer.setValue(Red);
767 Prefs::setStartcolouryellow(false);
768 Prefs::setStartcolourred(true);
769 qCDebug(KFOURINLINE_LOG) << "Setting startplayer to RED";
770 }
771 else
772 {
773 mStartPlayer.setValue(Yellow);
774 Prefs::setStartcolouryellow(true);
775 Prefs::setStartcolourred(false);
776 qCDebug(KFOURINLINE_LOG) << "Setting startplayer to YELLOW";
777 }
778 Prefs::self()->save();
779
780 return (COLOUR)mStartPlayer.value();
781 }
782
783
784 // Retrieve the current move number.
getCurrentMove()785 int KWin4Doc::getCurrentMove()
786 {
787 return mCurrentMove;
788 }
789
790
791 // Retrieve the maximum move number before undo
getMaxMove()792 int KWin4Doc::getMaxMove()
793 {
794 return mMaxMove;
795 }
796
797
798 // Retrieve the amount of history moves stored
getHistoryCnt()799 int KWin4Doc::getHistoryCnt()
800 {
801 return mHistoryCnt;
802 }
803
804
805 // Return the filename of the computer player AI process.
findProcessName()806 QString KWin4Doc::findProcessName()
807 {
808 // Try whether we run from a development source dir
809 #ifndef NDEBUG
810 #ifdef SRC_DIR
811 QString srcname = QStringLiteral(SRC_DIR)+QStringLiteral("/src/kfourinlineproc");
812 QFile fsrc(srcname);
813 if (fsrc.exists())
814 {
815 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Found SRC_DIR process" << srcname;
816 return srcname;
817 }
818 #endif
819 #endif
820
821
822 // First try a local dir override
823 QDir dir;
824 // TODO: This local filename is not found!!
825 QString filename=dir.path()+QStringLiteral("/kwin4/kfourinlineproc");
826 qCDebug(KFOURINLINE_LOG) << "PROC FILENAME="<<filename;
827 QFile flocal(filename);
828 if (flocal.exists())
829 {
830 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Found local process" << filename;
831 return filename;
832 }
833 QString path= QStandardPaths::findExecutable(QStringLiteral("kfourinlineproc"));
834 if (!path.isNull())
835 {
836 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Found system process" << path;
837 return path;
838 }
839 QString empty;
840 qCCritical(KFOURINLINE_LOG) << "Could not locate the computer player";
841 return empty;
842 }
843
844
845 // Debug: Listen to messages
networkMessageUpdate(int,quint32,quint32)846 void KWin4Doc::networkMessageUpdate(int /*id*/,quint32 /*sender*/,quint32 /*recv*/)
847 {
848 // qCDebug(KFOURINLINE_LOG) << "MSG: id=" << id << "sender=" << sender << "receiver="<<recv;
849 }
850
851
852 // Create a KPlayer
createPlayer(int,int io,bool isvirtual)853 KPlayer *KWin4Doc::createPlayer(int /*rtti*/, int io, bool isvirtual)
854 {
855 KWin4Player *player = new KWin4Player;
856 if (!isvirtual)
857 createIO(player,(KGameIO::IOMode)io);
858
859 connect(player, &KWin4Player::signalPropertyChanged, this, &KWin4Doc::playerPropertyChanged);
860 player->setStatus(mStatus);
861 return player;
862 }
863
864
865 // Called when a player input is received from the KGame object
866 // this is e.g. a mouse event, the AI or the network
playerInput(QDataStream & msg,KPlayer *)867 bool KWin4Doc::playerInput(QDataStream& msg, KPlayer* /*player*/)
868 {
869 qint32 move, pl;
870 msg >> pl >> move;
871 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::playerInput: ================ pl="<<pl<<" and move=" << move << "====================";
872
873 // Perform move and check for success
874 if (!doMove(move,pl))
875 {
876 // Repeat the same input
877 QTimer::singleShot(0, this,&KWin4Doc::repeatMove);
878 }
879
880 return false;
881 }
882
883
884 // Reactivate player in case of a move which could not pe performed.
repeatMove()885 void KWin4Doc::repeatMove()
886 {
887 getPlayer(getCurrentPlayer())->setTurn(true);
888 }
889
890
891 // Query the IO mode of player og the given color.
playedBy(int col)892 KGameIO::IOMode KWin4Doc::playedBy(int col)
893 {
894 return mPlayedBy[col];
895 }
896
897
898 // Sets the input device mode for the given player color.
setPlayedBy(int col,KGameIO::IOMode io)899 void KWin4Doc::setPlayedBy(int col, KGameIO::IOMode io)
900 {
901 if (global_debug>1)
902 qCDebug(KFOURINLINE_LOG) << " KWin4Doc::setPlayedBy(int "<<col<<",KGameIO::IOMode "<<io<<")";
903
904 KWin4Player *player=getPlayer((COLOUR)col);
905
906 // Modes for the score sprite
907 player->status()->setPlayedBy((int)io,player->userId());
908
909 if (mPlayedBy[col]!=io && !player->isVirtual())
910 {
911 bool myTurn = player->myTurn();
912 player->setTurn(false); // turn of move
913 mPlayedBy[col]=io;
914 player->removeGameIO(); // remove all IO's
915 createIO(player,io);
916 player->setTurn(myTurn); // turn on move
917 }
918 }
919
920
921 // Get the io values right after a load game as the io the playedby
922 // is not set there.
recalcIO()923 void KWin4Doc::recalcIO()
924 {
925 mPlayedBy[Yellow]=(KGameIO::IOMode)getPlayer(Yellow)->calcIOValue();
926 mPlayedBy[Red]=(KGameIO::IOMode)getPlayer(Red)->calcIOValue();
927 }
928
929
930 // Create player input devicea (KGame)
createIO(KPlayer * player,KGameIO::IOMode io)931 void KWin4Doc::createIO(KPlayer* player, KGameIO::IOMode io)
932 {
933 if (!player)
934 return;
935
936 if (global_debug>1)
937 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::createIO(KPlayer *player("<<player->userId()<<"),KGameIO::IOMode "<<io<<") ";
938
939 if (io&KGameIO::MouseIO)
940 {
941 KGameMouseIO *input;
942 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating MOUSE IO to "<<pView;
943 // We want the player to work over mouse. So please leave the "true" for mouse
944 // tracking on !!!
945 input=new KGameMouseIO(pView->viewport(), true);
946 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "MOUSE IO added";
947 // Connect mouse input to a function to process the actual input
948 connect(input, &KGameMouseIO::signalMouseEvent, pView, &KWin4View::mouseInput);
949 player->addGameIO(input);
950 }
951 else if (io&KGameIO::ProcessIO)
952 {
953 QString file=findProcessName();
954 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating PROCESS IO" << file;
955
956 KGameProcessIO *input;
957 // We want a computer player
958 input=new KGameProcessIO(file);
959 // Connect computer player to the setTurn
960 connect(input, &KGameProcessIO::signalPrepareTurn, this, &KWin4Doc::prepareAITurn);
961
962 connect(input, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAICommand);
963
964 connect(input, &KGameProcessIO::signalReceivedStderr, this, &KWin4Doc::receivedStderr);
965 player->addGameIO(input);
966 }
967 else if (io&KGameIO::KeyIO)
968 {
969 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating KEYBOARD IO";
970 // We want the player to work over keyboard
971 KGameKeyIO *input;
972 input=new KGameKeyIO(pView->parentWidget());
973 // Connect keys input to a function to process the actual input
974 connect((KGameKeyIO *)input,&KGameKeyIO::signalKeyEvent,
975 pView,&KWin4View::keyInput);
976 player->addGameIO(input);
977 }
978 }
979
receivedStderr(const QString & s)980 void KWin4Doc::receivedStderr(const QString &s)
981 {
982 if (global_debug>0)
983 qCDebug(KFOURINLINE_LOG) << "##### AI:" << s;
984 }
985
986
987 // This slot is called when a computer move should be generated
prepareAITurn(QDataStream & stream,bool b,KGameIO * input,bool * sendit)988 void KWin4Doc::prepareAITurn(QDataStream& stream, bool b, KGameIO* input, bool* sendit)
989 {
990 if (global_debug>1)
991 qCDebug(KFOURINLINE_LOG) << "KWin4Doc::prepareAITurn b="<<b;
992
993 // Set defaults
994 *sendit = false;
995
996 // Our player
997 KPlayer* player=input->player();
998 if (!player->myTurn()) return ;
999 if (!b) return ; // only create move on setTurn(true)
1000
1001 qint32 pl;
1002 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "slotPrepareComputerTurn for player id=" << player->id();
1003 pl=player->userId();
1004
1005 // Pack the game into the message
1006 prepareGameMessage(stream,pl);
1007
1008 // Do send
1009 *sendit=true;
1010 }
1011
1012 // Sends the current game status to the computer player
1013 // Careful: The data needs to be exactly the same as the computer
1014 // player reading on the other side
prepareGameMessage(QDataStream & stream,qint32 pl)1015 void KWin4Doc::prepareGameMessage(QDataStream& stream, qint32 pl)
1016 {
1017 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << " sending col=" << pl;
1018 stream << pl ;
1019 // This needs to be the same than the computer player reads!
1020 stream << (qint32)getCurrentMove();
1021 stream << (qint32)getCurrentPlayer();
1022 stream << (qint32)getPlayerColour(0);
1023 stream << (qint32)getPlayerColour(1);
1024 stream << (qint32)Prefs::level();
1025
1026 bool learning = Prefs::learning();
1027 // Allow learning only for one AI
1028 if ( mPlayedBy[Yellow] == KGameIO::ProcessIO &&
1029 mPlayedBy[Red] == KGameIO::ProcessIO &&
1030 pl == Yellow) learning = false;
1031 stream << (qint32)learning;
1032
1033 // Where to save the learn cache
1034 QString learnPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("kwin4");
1035
1036 stream << learnPath;
1037
1038 int i,j;
1039 for (i=0;i<FIELD_SIZE_Y;++i)
1040 {
1041 for (j=0;j<FIELD_SIZE_X;++j)
1042 {
1043 qint8 col;
1044 col=getColour(j,i);
1045 stream << col;
1046 }
1047 if (global_debug>1) qCDebug(KFOURINLINE_LOG)
1048 << getColour(0,i) << " "
1049 << getColour(1,i) << " "
1050 << getColour(2,i) << " "
1051 << getColour(3,i) << " "
1052 << getColour(4,i) << " "
1053 << getColour(5,i) << " "
1054 << getColour(6,i);
1055 }
1056 stream << (qint32)421256;
1057 }
1058
1059
1060 // The AI send a command, e.g. the game value to us
processAICommand(QDataStream & in,KGameProcessIO * io)1061 void KWin4Doc::processAICommand(QDataStream& in, KGameProcessIO* io)
1062 {
1063 qint8 cid;
1064
1065 // Receive command
1066 in >> cid;
1067 switch(cid)
1068 {
1069 case 1: // game value
1070 {
1071 AIBoard aiBoard;
1072 qint32 value, moveNo, level;
1073 in >> value >> moveNo >> level >> aiBoard;
1074 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks move" << moveNo << "value is" << value;
1075 // Store AI data
1076 mAIValues[moveNo] = value;
1077 setScore(value);
1078
1079 // Check AI mistakes
1080 if (moveNo>=2)
1081 {
1082 long delta = mAIValues[moveNo]-mAIValues[moveNo-2];
1083
1084 // Send game to process
1085 QByteArray buffer;
1086 QDataStream outstream(&buffer, QIODevice::WriteOnly);
1087 // Send last board to learn with current value
1088 outstream << aiBoard << value << (qint32)delta << level;
1089 io->sendMessage(outstream, 3, 0, gameId());
1090 }
1091 }
1092 break;
1093 default:
1094 qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAICommand: Unknown id" << cid;
1095 break;
1096 }
1097 }
1098
1099
1100 // This slot is called by the signal of KGame to indicated
1101 // that the network connection is done and a new client is
1102 // connected
1103 // cid is the id of the client connected. if this is equal
1104 // gameId() WE are the client
clientConnected(quint32 cid,KGame *)1105 void KWin4Doc::clientConnected(quint32 cid, KGame* /* me */)
1106 {
1107 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "void KWin4Doc::clientConnected id="<<cid << "we=" <<
1108 gameId() << "we admin=" << isAdmin() << "master)" << isMaster();
1109
1110 if (playerList()->count()!=2)
1111 {
1112 qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: We do not have two players...Trying to disconnect!";
1113 disconnect();
1114 return ;
1115 }
1116
1117 // Get the two players - more are not possible
1118 KWin4Player* p1=(KWin4Player *)playerList()->at(0);
1119 KWin4Player* p2=(KWin4Player *)playerList()->at(1);
1120 if (!p1->isVirtual())
1121 {
1122 Q_EMIT signalChatChanged(p1);
1123 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "CHAT to player 0";
1124 }
1125 else
1126 {
1127 Q_EMIT signalChatChanged(p2);
1128 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "CHAT to player 1";
1129 }
1130
1131 // Now check whose turn it is. The Admin will rule this
1132 if (isAdmin())
1133 {
1134 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "WE are ADMIN == COOL ! ";
1135 // p1 is local
1136 if (!p1->isVirtual())
1137 {
1138 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "p1 id=" << p1->userId() << "is local turn="<<p1->myTurn();
1139 // Exclusive setting of the turn
1140 p1->setTurn(p1->myTurn(),true);
1141 p2->setTurn(!p1->myTurn(),true);
1142 }
1143 else if (!p2->isVirtual())
1144 {
1145 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "p2 id=" << p2->userId() << "is local turn="<<p2->myTurn();
1146 // Exclusive setting of the turn
1147 p2->setTurn(p2->myTurn(),true);
1148 p1->setTurn(!p2->myTurn(),true);
1149 }
1150 }
1151 }
1152
1153
1154 // Get the KPlayer from the color by searching all players
1155 // users id's
getPlayer(COLOUR col)1156 KWin4Player* KWin4Doc::getPlayer(COLOUR col)
1157 {
1158 for (KGamePlayerList::const_iterator it = playerList()->constBegin(); it!= playerList()->constEnd(); ++it )
1159 {
1160 if ((*it)->userId()==col)
1161 return (KWin4Player *)(*it);
1162 }
1163 qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: Cannot find player with colour" << col << ". CRASH imminent";
1164 return nullptr;
1165 }
1166
1167
1168 // We create a process which calculates a computer move which is shown as hint to the player.
calculateHint()1169 void KWin4Doc::calculateHint()
1170 {
1171 // We allocate the hint process only if it is needed
1172 if (!mHintProcess)
1173 {
1174 QString file = findProcessName();
1175 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating HINT PROCESS";
1176
1177 // We want a computer player
1178 mHintProcess=new KGameProcessIO(file);
1179
1180 connect(mHintProcess, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAIHintCommand);
1181 }
1182
1183 // Send game to process
1184 qint32 pl;
1185 QByteArray buffer;
1186 QDataStream stream(&buffer, QIODevice::WriteOnly);
1187 pl=getCurrentPlayer();
1188 prepareGameMessage(stream, pl);
1189 mHintProcess->sendMessage(stream, 2, 0, gameId());
1190 }
1191
1192
1193 // The compute process sent a hint which we show in the game board.
processAIHintCommand(QDataStream & in,KGameProcessIO *)1194 void KWin4Doc::processAIHintCommand(QDataStream& in,KGameProcessIO* /*io*/)
1195 {
1196 qint8 cid;
1197 // Read command
1198 in >> cid;
1199 switch(cid)
1200 {
1201 case 2: // Hint
1202 {
1203 qint32 pl;
1204 qint32 move;
1205 qint32 value;
1206 // Read parameters of command
1207 in >> pl >> move >> value;
1208 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks pl=" << pl << "move =" << move;
1209 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks hint is" << move << "and value is" << value;
1210
1211 // Display hint
1212 int x = move;
1213 int y = mFieldFilled.at(x);
1214 pView->displayHint(x,y);
1215 }
1216 break;
1217 default:
1218 qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAIHintCommand: Unknown id" << cid;
1219 break;
1220 }
1221 }
1222
1223
1224 // Called when a player property has changed. We check whether the name
1225 // changed and then update the score widget
1226 // We should maybe do this for the other properties too to update
1227 // the status widget...I am not sure here...we'll see
playerPropertyChanged(KGamePropertyBase * prop,KPlayer * player)1228 void KWin4Doc::playerPropertyChanged(KGamePropertyBase* prop,KPlayer* player)
1229 {
1230 if (!pView) return ;
1231
1232 // Check for name changes
1233 if (prop->id()==KGamePropertyBase::IdName)
1234 {
1235 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Player name id=" << player->userId() << "changed to" << player->name();
1236 mStatus->setPlayerName(player->name(),player->userId());
1237 }
1238
1239 }
1240
1241
1242 // Called by KGame when a game property has changed.
gamePropertyChanged(KGamePropertyBase * prop,KGame *)1243 void KWin4Doc::gamePropertyChanged(KGamePropertyBase* prop, KGame* /* me */)
1244 {
1245 if (!pView) return;
1246
1247 // Move number
1248 if (prop->id()==mCurrentMove.id())
1249 {
1250 // TODO pView->scoreWidget()->setMove(mCurrentMove);
1251 }
1252
1253 // Computer AI value
1254 else if (prop->id()==mScore.id())
1255 {
1256 int sc=mScore/10000;
1257 if (sc==0 && mScore.value()>0) sc=1;
1258 else if (sc==0 && mScore.value()<0) sc=-1;
1259 // TODO pView->scoreWidget()->setChance(sc);
1260 }
1261
1262 // Whose turn is it
1263 else if (prop->id()==mAmzug.id())
1264 {
1265 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Amzug changed to" << mAmzug.value();
1266 mStatus->setTurn(mAmzug);
1267 }
1268
1269 // The game status
1270 else if (prop->id()==KGamePropertyBase::IdGameStatus)
1271 {
1272 if (gameStatus()==Abort)
1273 {
1274 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game abort +++";
1275 Q_EMIT signalGameOver(2,getPlayer(getCurrentPlayer()),this); // 2 indicates Abort
1276 }
1277 else if (gameStatus()==Run)
1278 {
1279 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run +++";
1280 if (playerList()->count()==2)
1281 {
1282 activateCurrentPlayer(); // Set the current player to play
1283 Q_EMIT signalGameRun();
1284 }
1285 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run done +++";
1286 }
1287 else if (gameStatus()==Init)
1288 {
1289 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game INIT +++";
1290 resetGame(true);
1291 }
1292 else if (gameStatus()==End)
1293 {
1294 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game END +++";
1295 }
1296 else
1297 {
1298 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::other status signal +++";
1299 }
1300
1301 }
1302 }
1303
1304
1305 // This is an overwritten function of KGame which is called
1306 // when a game is loaded. This can either be via a network
1307 // connect or via a real load from file
loadgame(QDataStream & stream,bool network,bool reset)1308 bool KWin4Doc::loadgame(QDataStream &stream,bool network,bool reset)
1309 {
1310 if (global_debug>1)
1311 qCDebug(KFOURINLINE_LOG) << "loadgame() network=" << network << "reset="<< reset;
1312 if (!network) setGameStatus(End);
1313
1314 // Clear out the old game
1315 if (global_debug>1) qCDebug(KFOURINLINE_LOG)<<"loadgame wants to reset the game";
1316 resetGame(true);
1317
1318 // load the new game
1319 bool res=KGame::loadgame(stream,network,reset);
1320 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amzug loaded to ="<<mAmzug.value();
1321
1322 // Replay the game be undoing and redoing
1323 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "REDRAW GAME using undo/redo";
1324 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "history cnt="<<mHistoryCnt.value();
1325 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amzug ="<<mAmzug.value();
1326 int cnt=0;
1327 while(undoMove())
1328 {
1329 ++cnt;
1330 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Undoing move "<<cnt;
1331 }
1332 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amzug ="<<mAmzug.value();
1333 while(cnt>0)
1334 {
1335 redoMove();
1336 --cnt;
1337 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Redoing move "<<cnt;
1338 }
1339 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amzug ="<<mAmzug.value();
1340
1341 // Set the input devices
1342 recalcIO();
1343 // And set the right player to turn
1344 activateCurrentPlayer();
1345
1346 if (global_debug>1)
1347 qCDebug(KFOURINLINE_LOG) << "loadgame done +++";
1348 return res;
1349 }
1350
1351
1352 // This is also an overwritten function of KGame. It is
1353 // Called in the game negotiation upon connect. Here
1354 // the games have to determine what player is remote and
1355 // what is local
1356 // This function is only called in the Admin.
newPlayersJoin(KGamePlayerList *,KGamePlayerList * newList,QList<int> & inactivate)1357 void KWin4Doc::newPlayersJoin(KGamePlayerList* /*oldList*/,KGamePlayerList* newList,QList<int> &inactivate)
1358 {
1359 if (global_debug>1)
1360 qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: START";
1361
1362 KWin4Player *yellow=getPlayer(Yellow);
1363 KWin4Player *red=getPlayer(Red);
1364 // Take the master player with the higher priority. Priority is set
1365 // by the network dialog
1366 if (yellow->networkPriority()>red->networkPriority())
1367 {
1368 // Deactivate the lower one
1369 inactivate.append(red->id());
1370 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "ADMIN keeps yellow and kicks red=" << red->id()<<" userId/col="<<red->userId();
1371 // loop all client players and deactivate the one which have the color
1372 // yellow
1373 for ( KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it )
1374 {
1375 KPlayer *player = *it;
1376 if (player->userId()==yellow->userId())
1377 {
1378 inactivate.append(player->id());
1379 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Deactivate C1" << player->id()<<" col="<<player->userId();
1380 }
1381 }
1382 }
1383 else
1384 {
1385 // Deactivate the lower one
1386 inactivate.append(yellow->id());
1387 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "ADMIN keeps red and kicks yellow=" << yellow->id()<<" userId/col="<<yellow->userId();
1388 // loop all client players and deactivate the one which have the color
1389 // red
1390 for ( KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it )
1391 {
1392 KPlayer *player = *it;
1393 if (player->userId()==red->userId())
1394 {
1395 inactivate.append(player->id());
1396 if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Deactivate C2" << player->id()<<" col="<<player->userId();
1397 }
1398 }
1399 }
1400 if (global_debug>1)
1401 qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: DONE";
1402 }
1403
1404
1405