File indexing completed on 2024-04-28 04:02:09

0001 /*
0002     This file is part of the KDE games kwin4 program
0003     SPDX-FileCopyrightText: 2000 Martin Heni <kde@heni-online.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kwin4proc.h"
0009 
0010 // KDEGames
0011 #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API
0012 #include <libkdegamesprivate/kgame/kgamemessage.h>
0013 // Qt
0014 #include <QByteArray>
0015 #include <QDataStream>
0016 #include <QElapsedTimer>
0017 #include <QFile>
0018 #include <QMutex>
0019 #include <QRandomGenerator>
0020 #include <QWaitCondition>
0021 // Std
0022 #include <cstdio>
0023 #include <cstdlib>
0024 #include <string>
0025 
0026 // Algorithm defines
0027 #define MIN_TIME 1000 // min time in milli sec for move
0028 //#define MIN_TIME        10           // min time in milli sec for move
0029 
0030 // Board and game geometry
0031 #define MAX_PIECES_COL 6 // Max 6 pieces per column
0032 #define WIN4 4 // 4 in a row won
0033 #define MAX_MOVE 42 // Maximum so many moves possible
0034 #define FIELD_OFFSET 10 // Offset
0035 
0036 // AI rating and routines
0037 #define LOWERT -999999999L // Init variables with this value
0038 #define VICTORY_VALUE 9999999L // Win or loss value
0039 #define MAX_EXTRA_RECURSION 15 // Maximum so many extra recursions
0040 #define PERCENT_FOR_INC_ITERATION 40 // If less than this amount of estimated moves are
0041 #define EVAL_RANDOM                                                                                                                                            \
0042     2500 // Randomize position evaluation by this
0043          // calculated increase recursion
0044 
0045 // AI Learning
0046 #define LEARN_DELTA -15000L // Learn if position drops by more than this
0047 #define MAX_LEARNED_POSITIONS 100000 // Learn not more than this positions
0048 #define MIN_LEARN_LEVEL 3 // Use learning only >= this level
0049 #define BRAIN_VERSION 1 // Version number
0050 
0051 // Constructor: Setup AI
0052 KComputer::KComputer()
0053 {
0054     const char *s1 = "7777776666666123456654321123456654321";
0055     const char *s2 = "0000000000000000000123456000000123456";
0056     unsigned int i;
0057 
0058     // Init variables
0059     for (i = 0; i < strlen(s1); ++i)
0060         mRowLengths[i] = s1[i] - '0';
0061     for (i = 0; i < strlen(s2); ++i)
0062         mStartOfRows[i] = s2[i] - '0';
0063 
0064     // Unknown yet how fast AI calculates
0065     mCalcPosPerMS = -1.0;
0066 
0067     // No brain loaded yet
0068     mBrainLoaded = false;
0069     mBrainUsed = 0;
0070 
0071     // Connect signals of KGame framework
0072     connect(&proc, &KGameProcess::signalCommand, this, &KComputer::slotCommand);
0073     connect(&proc, &KGameProcess::signalInit, this, &KComputer::slotInit);
0074     connect(&proc, &KGameProcess::signalTurn, this, &KComputer::slotTurn);
0075 }
0076 
0077 // Received init command (unused)
0078 void KComputer::slotInit(QDataStream & /*in */, int /*id*/)
0079 {
0080     /*
0081     QByteArray buffer;
0082     QDataStream out(buffer,QIODevice::WriteOnly);
0083     int msgid=KGameMessage::IdProcessQuery;
0084     out << (int)1;
0085     proc.sendSystemMessage(out,msgid,0);
0086     */
0087 }
0088 
0089 // Received turn command
0090 void KComputer::slotTurn(QDataStream &in, bool turn)
0091 {
0092     QByteArray buffer;
0093     QDataStream out(&buffer, QIODevice::WriteOnly);
0094     // fprintf(stderr,"  KComputer::slotTurn(turn=%d)\n",turn);
0095     // fflush(stderr);
0096     if (turn) {
0097         // Create a move
0098 
0099         MoveResult result = think(in, out, false);
0100         int id = KGameMessage::IdPlayerInput;
0101         proc.sendSystemMessage(out, id, 0);
0102         sendValue(result.value, mCurMoveNo);
0103         // fprintf(stderr,"  KComputer::slotTurn sending value (value=%ld)\n",result.value);
0104         // fflush(stderr);
0105     }
0106 }
0107 
0108 // Send position data back to main program
0109 void KComputer::sendValue(long value, int moveNo)
0110 {
0111     qint8 cid = 1; // notifies our KGameIO that this is a value message
0112     int id = KGameMessage::IdProcessQuery;
0113     QByteArray buffer;
0114     QDataStream out(&buffer, QIODevice::WriteOnly);
0115     out << cid << (qint32)value << (qint32)moveNo << (qint32)mLevel << mMaxAIBoard;
0116     proc.sendSystemMessage(out, id, 0);
0117     // fprintf(stderr,"  KComputer::sendValue (value=%ld)\n",value);
0118     // fflush(stderr);
0119 }
0120 
0121 // Load brain position cache
0122 void KComputer::loadBrain()
0123 {
0124     QFile file(mBrainDir + QStringLiteral("kwin4.brain"));
0125     if (!file.open(QIODevice::ReadOnly)) {
0126         fprintf(stderr, "  KComputer::Brain file cannot be opened.\n");
0127         fflush(stderr);
0128         return;
0129     }
0130 
0131     QDataStream in(&file);
0132     qint32 version;
0133     in >> version;
0134     if (version != (qint32)BRAIN_VERSION) {
0135         fprintf(stderr, "  KComputer::Loading brain version error %ld\n", (long)version);
0136         fflush(stderr);
0137         file.close();
0138         return;
0139     }
0140 
0141     qint32 noOfItems;
0142     in >> noOfItems;
0143     fprintf(stderr, "  KComputer::Loading %ld brain data\n", (long)noOfItems);
0144     fflush(stderr);
0145 
0146     bool erase = false;
0147     if (noOfItems > MAX_LEARNED_POSITIONS)
0148         erase = true;
0149 
0150     for (int cnt = 0; cnt < noOfItems; ++cnt) {
0151         AIBoard board;
0152         AIValue value;
0153         in >> board >> value;
0154 
0155         // Forget 10% positions in case of overload
0156         if (erase && cnt % 10 != 0) {
0157             mBrain.insert(board, value);
0158         }
0159     }
0160     fflush(stderr);
0161     qint32 check;
0162     in >> check;
0163     file.close();
0164 
0165     if (check != (qint32)0x18547237) {
0166         fprintf(stderr, "  KComputer::Loading brain CRC error %ld cnt=%d\n", (long)check, noOfItems);
0167         fflush(stderr);
0168         mBrain.clear();
0169         return;
0170     }
0171     fprintf(stderr, "  KComputer::Loading brain (%d items) succeeded\n", (int)noOfItems);
0172     fflush(stderr);
0173 }
0174 
0175 // Save brain position cache
0176 void KComputer::saveBrain()
0177 {
0178     QFile file(mBrainDir + QStringLiteral("kwin4.brain"));
0179     if (!file.open(QIODevice::WriteOnly)) {
0180         fprintf(stderr, "  KComputer::saving brain failed.\n");
0181         fflush(stderr);
0182         return;
0183     }
0184 
0185     QDataStream out(&file);
0186     out << (qint32)BRAIN_VERSION; // Version
0187     out << (qint32)mBrain.size();
0188 
0189     QHashIterator<AIBoard, AIValue> it(mBrain);
0190     while (it.hasNext()) {
0191         it.next();
0192         AIBoard board = it.key();
0193         AIValue value = it.value();
0194         out << board << value;
0195     }
0196     out << (qint32)0x18547237; // Checksum
0197     file.close();
0198     fprintf(stderr, "  KComputer:: Brain saved.\n");
0199     fflush(stderr);
0200 }
0201 
0202 // Received a command from the main program
0203 void KComputer::slotCommand(QDataStream &in, int msgid, int /*receiver*/, int /*sender*/)
0204 {
0205     // fprintf(stderr,"KComputer::slotCommand(Msgid=%d)\n",msgid);
0206     // fflush(stderr);
0207     QByteArray buffer;
0208     QDataStream out(&buffer, QIODevice::WriteOnly);
0209     switch (msgid) {
0210     case 2: // hint
0211     {
0212         qint8 cid = 2;
0213         quint32 recv = 0;
0214         out << cid;
0215         MoveResult result = think(in, out, true);
0216         out << (qint32)result.value;
0217         int id = KGameMessage::IdProcessQuery;
0218         proc.sendSystemMessage(out, id, recv);
0219     } break;
0220     case 3: // AI board value changed, maybe learn
0221     {
0222         AIBoard aiBoard;
0223         qint32 value, level, delta;
0224         in >> aiBoard >> value >> delta >> level;
0225         // Only learn big changes
0226         if (mIsLearning && delta < LEARN_DELTA && level >= MIN_LEARN_LEVEL && level < MIN_LEARN_LEVEL + AIValue::NO_OF_LEVELS) {
0227             fprintf(stderr, "KComputer:: LEARNING Received AI board for value %ld delta %ld level %ld\n", (long)value, (long)delta, (long)level);
0228             aiBoard.print();
0229 
0230             // Learn board?
0231             AIValue aiValue;
0232             // Do we already store board?
0233             if (mBrain.contains(aiBoard)) {
0234                 aiValue = mBrain.value(aiBoard);
0235             }
0236             // Do we store mirror board?
0237             else if (mBrain.contains(aiBoard.mirror())) {
0238                 aiBoard = aiBoard.mirror();
0239                 aiValue = mBrain.value(aiBoard);
0240             }
0241             // Set not set or decreased values in value structure
0242             if (!aiValue.isSet((int)level - MIN_LEARN_LEVEL) || aiValue.value((int)level - MIN_LEARN_LEVEL) > (long)value) {
0243                 aiValue.setValue((int)level - MIN_LEARN_LEVEL, (long)value);
0244                 mBrain.insert(aiBoard, aiValue);
0245                 saveBrain();
0246                 fprintf(stderr, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
0247                 fprintf(stderr, "$        LEARNING                          $\n");
0248                 fprintf(stderr, "$ Setting board level %d to value = %ld\n", (int)level, (long)value);
0249                 fprintf(stderr, "$ mBrain size=%" PRIdQSIZETYPE "\n", mBrain.size());
0250                 fprintf(stderr, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
0251                 fflush(stderr);
0252             } else // TODO: Remove else
0253             {
0254                 fprintf(stderr, "NOT LEARNING $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
0255                 fprintf(stderr,
0256                         "     REASON: %d %ld %ld\n",
0257                         aiValue.isSet((int)level - MIN_LEARN_LEVEL),
0258                         aiValue.value((int)level - MIN_LEARN_LEVEL),
0259                         (long)value);
0260                 fflush(stderr);
0261             }
0262         } // end delta
0263     } break;
0264     default:
0265         fprintf(stderr, "KComputer:: unknown command (msgid=%d)\n", msgid);
0266         fflush(stderr);
0267     }
0268 }
0269 
0270 // Think up a move (plus reading data from stream)
0271 KComputer::MoveResult KComputer::think(QDataStream &in, QDataStream &out, bool /*hint*/)
0272 {
0273     qint32 pl;
0274     qint32 move;
0275     qint32 tmp;
0276     COLOUR secondPlayer;
0277     // Which color's move is to come
0278     COLOUR currentPlayer;
0279     // Which player started the game
0280     COLOUR startPlayer;
0281 
0282     // Read command data stream into local variables
0283     in >> pl;
0284     in >> tmp;
0285     mCurMoveNo = tmp;
0286     in >> tmp;
0287     currentPlayer = (COLOUR)(tmp);
0288     in >> tmp;
0289     startPlayer = (COLOUR)(tmp);
0290     in >> tmp;
0291     secondPlayer = (COLOUR)(tmp);
0292     in >> tmp;
0293     mLevel = tmp;
0294     in >> tmp;
0295     mIsLearning = tmp;
0296     in >> mBrainDir;
0297 
0298     // Check proper learning level. If not proper switch learning off.
0299     if (mLevel < MIN_LEARN_LEVEL || mLevel >= MIN_LEARN_LEVEL + AIValue::NO_OF_LEVELS) {
0300         mIsLearning = false;
0301         fprintf(stderr, "KComputer:: Switching off learning for level %d\n", mLevel);
0302     }
0303 
0304     fprintf(stderr,
0305             "KComputer::think: pl=%d, mCurMoveNo=%d currentPlayer=%d begin=%d secondPlayer=%d level=%d learn=%d\n",
0306             pl,
0307             mCurMoveNo,
0308             currentPlayer,
0309             startPlayer,
0310             secondPlayer,
0311             mLevel,
0312             mIsLearning);
0313     // fprintf(stderr,"KComputer::learning=%d path=%s\n",mIsLearning,mBrainDir.toLatin1().data());
0314 
0315     // Brain loaded?
0316     if (!mBrainLoaded && mIsLearning) {
0317         mBrainLoaded = true;
0318         loadBrain();
0319     }
0320 
0321     // Setup board (init)
0322     // The game board prepared for the AI
0323     FARBE fieldMatrix[SIZE_Y_ALL + 1][SIZE_X + 1];
0324     // Amount of pieces on the game board
0325     char numberMatrix[SIZE_Y_ALL + 1];
0326 
0327     for (int y = 0; y <= SIZE_Y_ALL; ++y) {
0328         numberMatrix[y] = 0;
0329     }
0330 
0331     for (int y = 0; y <= SIZE_Y; ++y) {
0332         for (int x = 0; x <= SIZE_X; ++x) {
0333             fieldMatrix[y][x] = (FARBE)(y + FIELD_OFFSET);
0334             fieldMatrix[6 + x][y] = (FARBE)(y + FIELD_OFFSET);
0335             fieldMatrix[13 + x + y][x] = (FARBE)(y + FIELD_OFFSET);
0336             fieldMatrix[30 + x - y][x] = (FARBE)(y + FIELD_OFFSET);
0337         } // next x
0338     } // next y
0339 
0340     // Field comes as 42 qint8's representing moves
0341     int i, j;
0342     for (i = 0; i <= SIZE_Y; ++i) {
0343         for (j = 0; j <= SIZE_X; ++j) {
0344             qint8 col;
0345             in >> col;
0346             DoMove(j, (COLOUR)col, fieldMatrix, numberMatrix);
0347         }
0348     }
0349 
0350     // Check final checksum
0351     in >> tmp;
0352     if ((long)tmp != 421256L) {
0353         fprintf(stderr, "CHECKSUM=%ld [should be 421256]\n", (long)tmp);
0354         fflush(stderr);
0355     }
0356 
0357     // Estimated number of positions to evaluate (MAX)
0358     int estimated = 0;
0359     for (int i = 1; i <= mLevel + 1; ++i) {
0360         estimated += int(pow(7., i));
0361     }
0362 
0363     // Measure time of move and positions evaluated
0364     QElapsedTimer timer;
0365     timer.start();
0366     mPosEvaluations = 0;
0367 
0368     // Get move
0369     int gameOver = 0;
0370     int extraRecurstion = 0;
0371     MoveResult result;
0372 
0373     // Loop movement if not many positions are evaluated (end game)
0374     // to look ahead a bit more
0375     do {
0376         // Actually calculate move
0377         result = MinMax(currentPlayer, fieldMatrix, numberMatrix, mLevel + extraRecurstion, mCurMoveNo, true);
0378         result.value = -result.value;
0379 
0380         // Do not recalculate for (nearly finished) games
0381         // if (result.value >= VICTORY_VALUE*0.95 || result.value <= -VICTORY_VALUE *0.95) gameOver = 1;
0382         extraRecurstion++;
0383     } while (PERCENT_FOR_INC_ITERATION / 10 * mPosEvaluations <= estimated && !gameOver && extraRecurstion < MAX_EXTRA_RECURSION);
0384 
0385     // Measure elapsed time
0386     int elapsed = timer.elapsed();
0387     if (elapsed < 1)
0388         elapsed = 1;
0389     mCalcPosPerMS = (float)mPosEvaluations / (float)elapsed;
0390 
0391     // Debug
0392     fprintf(stderr,
0393             "AI MOVE to %d value=%ld level %d took time=%d ms and evals=%d estimated=%d pos/ms=%f brain=%ld\n",
0394             result.move,
0395             result.value,
0396             mLevel,
0397             elapsed,
0398             mPosEvaluations,
0399             estimated,
0400             mCalcPosPerMS,
0401             mBrainUsed);
0402 
0403     // Sleep a minimum amount to slow down moves
0404     if (elapsed < MIN_TIME) {
0405         // is usleep(1000*(MIN_TIME-elapsed));
0406         QMutex mutex;
0407         QWaitCondition cond;
0408         mutex.lock();
0409         cond.wait(&mutex, MIN_TIME - elapsed);
0410 
0411         elapsed = timer.elapsed();
0412         fprintf(stderr, "AI after sleeping time elapsed=%d\n", elapsed);
0413     }
0414     fflush(stderr);
0415 
0416     // Send out move
0417     move = result.move;
0418     out << pl << move;
0419     return result;
0420 }
0421 
0422 // Min-Max AI algorithm
0423 KComputer::MoveResult KComputer::MinMax(COLOUR color, FARBE field[][SIZE_X + 1], char numbers[], int reklev, int moveNo, bool first)
0424 {
0425     // Modify move value
0426     static long gauss[] = {10, 50, 300, 500, 300, 50, 10};
0427 
0428     // Local board
0429     FARBE locField[SIZE_Y_ALL + 1][SIZE_X + 1];
0430     char locNumbers[SIZE_Y_ALL + 1];
0431 
0432     // Result of move
0433     MoveResult result;
0434     result.move = -1; // No move found
0435     result.value = LOWERT;
0436 
0437     for (int x = 0; x <= SIZE_X; ++x) {
0438         long wert;
0439         if (numbers[6 + x] >= MAX_PIECES_COL)
0440             continue;
0441 
0442         // Perform test move
0443         memcpy(locNumbers, numbers, sizeof(locNumbers));
0444         memcpy(locField, field, sizeof(locField));
0445         DoMove(x, color, locField, locNumbers);
0446 
0447         // Count evaluations
0448         mPosEvaluations++;
0449 
0450         // Check for game over
0451         COLOUR winner = isGameOver(field, numbers);
0452         if (winner != Nobody) {
0453             // Choose tree with fastest victory or slowest loss
0454             if (winner == color)
0455                 wert = VICTORY_VALUE + reklev * 5000;
0456             else
0457                 wert = -VICTORY_VALUE - reklev * 5000;
0458         }
0459         // Drawn / Remis
0460         else if (moveNo >= MAX_MOVE) {
0461             wert = 0;
0462         }
0463         // End of recursion reached
0464         else if (reklev <= 0) {
0465             wert = PositionEvaluation(color, field);
0466             // wert += gauss[x]; // TODO: Gauss should be here
0467             //  No need for filling the test board here
0468         }
0469         // MinMax calculation for next recursion level
0470         else {
0471             // Check learning
0472             AIBoard testBoard(color, locField);
0473             // Found cached board or not
0474             bool haveToCalculate = true;
0475 
0476             // Check for learned board
0477             if (mIsLearning && mBrain.contains(testBoard)) {
0478                 AIValue testValue = mBrain.value(testBoard);
0479                 if (testValue.isSet(mLevel - MIN_LEARN_LEVEL)) {
0480                     wert = testValue.value(mLevel - MIN_LEARN_LEVEL);
0481                     mBrainUsed++;
0482                     haveToCalculate = false;
0483                     fprintf(stderr, "$$$$$$ Board[%d] found in cache value = %ld used=%ld\n", mLevel, wert, mBrainUsed);
0484                     fflush(stderr);
0485                 }
0486             }
0487             // Check for learned mirror board
0488             else if (mIsLearning && mBrain.contains(testBoard.mirror())) {
0489                 testBoard = testBoard.mirror();
0490                 AIValue testValue = mBrain.value(testBoard);
0491                 if (testValue.isSet(mLevel - MIN_LEARN_LEVEL)) {
0492                     wert = testValue.value(mLevel - MIN_LEARN_LEVEL);
0493                     mBrainUsed++;
0494                     haveToCalculate = false;
0495                     fprintf(stderr, "$$$$$$ MIRROR 2 Board[%d] found in cache value = %ld  used=%ld\n", mLevel, wert, mBrainUsed);
0496                     fflush(stderr);
0497                 }
0498             }
0499 
0500             // Real calculation necessary
0501             if (haveToCalculate) {
0502                 // Swap color for next move
0503                 COLOUR nextColor;
0504                 if (color == Red)
0505                     nextColor = Yellow;
0506                 else
0507                     nextColor = Red;
0508                 MoveResult minmaxResult = MinMax(nextColor, locField, locNumbers, reklev - 1, moveNo + 1, false);
0509                 wert = minmaxResult.value + gauss[x]; // TODO: Remove gauss here
0510             }
0511         } // end else MinMax
0512 
0513         // New maximum?
0514         if (wert >= result.value) {
0515             result.value = wert;
0516             result.move = x;
0517             if (first)
0518                 mMaxAIBoard.fromField(color, false, locField);
0519             // if (result.value >= VICTORY_VALUE) break;
0520         }
0521     } // next x
0522 
0523     // MinMax
0524     result.value = -result.value;
0525     return result;
0526 } // end MinMax
0527 
0528 // Position evaluation
0529 long KComputer::PositionEvaluation(COLOUR curColor, FARBE field[][SIZE_X + 1])
0530 {
0531     /* Abstand:              0    1    2    3    4    5    */
0532     static long myWERT[] = {2200, 600, 300, 75, 20, 0};
0533     // static long myWERT[]={0,0,0,0,0,0};
0534     /* How many ofCOLOUR:       0     1     2     3    4 */
0535     static long steinWERT[4][5] = {
0536         {0, 500L, 40000L, 200000L, VICTORY_VALUE}, // Leerfelder=0
0537         {0, 500L, 8000L, 40000L, VICTORY_VALUE}, //           =1
0538         {0, 00L, 4000L, 25000L, VICTORY_VALUE}, //           =2
0539         {0, 00L, 2000L, 12500L, VICTORY_VALUE}, //           =3
0540     };
0541 
0542     // Initial values
0543     long yellow_value = random(EVAL_RANDOM);
0544     long red_value = random(EVAL_RANDOM);
0545 
0546     for (int y = 0; y <= SIZE_Y_ALL; ++y) {
0547         if (mRowLengths[y] < WIN4)
0548             continue;
0549         for (int i = 0; i <= (mRowLengths[y] - WIN4); ++i) {
0550             COLOUR color = Nobody;
0551             long value = 0;
0552             int cntcol = 0;
0553             int cnt = 0;
0554             for (int j = 0; j < WIN4; ++j) {
0555                 FARBE checkField = field[y][i + j + mStartOfRows[y]];
0556                 if ((COLOUR)checkField == Red) {
0557                     if (color == Yellow) {
0558                         color = Nobody;
0559                         break;
0560                     }
0561                     cntcol++;
0562                     color = Red;
0563                 } else if ((COLOUR)checkField == Yellow) {
0564                     if (color == Red) {
0565                         color = Nobody;
0566                         break;
0567                     }
0568                     cntcol++;
0569                     color = Yellow;
0570                 } else {
0571                     cnt += checkField - FIELD_OFFSET;
0572                     value += myWERT[checkField - FIELD_OFFSET];
0573                 }
0574             } /*next j */
0575             if (cnt > 3)
0576                 cnt = 3;
0577             if (color == Red)
0578                 red_value += (value + steinWERT[cnt][cntcol]);
0579             else if (color == Yellow)
0580                 yellow_value += (value + steinWERT[cnt][cntcol]);
0581         } /*next i*/
0582     } /*next y*/
0583 
0584     // Return value
0585     if (curColor == Red)
0586         return red_value - yellow_value;
0587     else
0588         return yellow_value - red_value;
0589 }
0590 
0591 // Check for game over
0592 COLOUR KComputer::isGameOver(FARBE field[][SIZE_X + 1], char numbers[])
0593 {
0594     for (int y = 0; y <= SIZE_Y_ALL; ++y) {
0595         if (numbers[y] < WIN4)
0596             continue;
0597         if (mRowLengths[y] < WIN4)
0598             continue;
0599 
0600         int cnt = 0;
0601         COLOUR thiscolor = Nobody;
0602         for (int x = 0; x < mRowLengths[y]; ++x) {
0603             COLOUR checkField = (COLOUR)field[y][x + mStartOfRows[y]];
0604             if (checkField == thiscolor)
0605                 ++cnt;
0606             else {
0607                 cnt = 1;
0608                 thiscolor = checkField;
0609             }
0610             if ((cnt >= WIN4) && ((thiscolor == Yellow) || (thiscolor == Red)))
0611                 return thiscolor;
0612         } // next x
0613     } // next y
0614     return Nobody;
0615 }
0616 
0617 // Execute move on given board
0618 void KComputer::DoMove(int move, COLOUR color, FARBE field[][SIZE_X + 1], char numbers[])
0619 {
0620     if (color == Tip || color == Nobody)
0621         return; // no real move
0622     int x = move;
0623     int y = numbers[6 + move];
0624     field[y][x] = color;
0625 
0626     field[6 + x][y] = color;
0627     field[13 + x + y][x] = color;
0628     field[30 + x - y][x] = color;
0629     numbers[y]++;
0630     numbers[6 + x]++;
0631     numbers[13 + x + y]++;
0632     numbers[30 + x - y]++;
0633     for (int i = y + 1; i <= SIZE_Y; ++i) {
0634         field[i][x]--;
0635         field[6 + x][i]--;
0636         field[13 + x + i][x]--;
0637         field[30 + x - i][x]--;
0638     }
0639 }
0640 
0641 // Retrieve random number 0..max
0642 int KComputer::random(int max)
0643 {
0644     // return 0;
0645     return proc.random()->bounded(max);
0646 }
0647 
0648 // Main startup
0649 int main()
0650 {
0651     // This is the computer player...it should do the calculation
0652     // It doesn't do much here
0653     fprintf(stderr, "AI process starts up\n");
0654     fflush(stderr);
0655     KComputer comp;
0656     // And start the event loop
0657     comp.proc.exec();
0658     fprintf(stderr, "AI process exists.\n");
0659     fflush(stderr);
0660     return 1;
0661 }
0662 
0663 #include "moc_kwin4proc.cpp"