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