• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

ksquares

aicontroller.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2006 by Matthew Williams    <matt@milliams.com>         *
00003  *                                                                         *
00004  *   This program is free software; you can redistribute it and/or modify  *
00005  *   it under the terms of the GNU General Public License as published by  *
00006  *   the Free Software Foundation; either version 2 of the License, or     *
00007  *   (at your option) any later version.                                   *
00008  ***************************************************************************/
00009 
00010 #include "aicontroller.h"
00011 
00012 #include <ctime>
00013 #include <kdebug.h>
00014 
00015 #include "settings.h"
00016 
00017 aiController::aiController(int newPlayerId, const QList<bool> &newLines, const QList<int> &newSquareOwners, int newWidth, int newHeight) : squareOwners(newSquareOwners), lines(newLines), playerId(newPlayerId), width(newWidth), height(newHeight)
00018 {
00019     srand( (unsigned)time( NULL ) );
00020     kDebug() << "AI: Starting AI...";
00021 }
00022 
00023 QList<int> aiController::autoFill(int safeMovesLeft)
00024 {
00025     QList<int> fillLines;
00026     
00027     // add a random safe moves while there are safe moves left
00028     QList<int> next;
00029     //kDebug() << safeMoves().isEmpty();
00030     while( !( (next = safeMoves()).isEmpty() ) )
00031     {
00032         int nextLine = next[rand() % next.size()];
00033         lines[nextLine] = true;
00034         //kDebug() << nextLine;
00035         fillLines << nextLine;
00036     }
00037     
00038     // safeMovesLeft times delete a line from fillLines
00039     for (int i = 0; i<safeMovesLeft; ++i)
00040     {
00041         if (fillLines.isEmpty()) break;
00042         int index = rand() % fillLines.size();
00043         fillLines.removeAt(index);
00044     }
00045     
00046     return fillLines;
00047 }
00048 
00049 int aiController::chooseLine() const
00050 {
00051     QList<int> choiceList;
00052     for(int i=0; i<lines.size(); i++)   //trying to get points. looking for squares with 3 lines
00053     {
00054         if(!lines.at(i))
00055         {
00056             QList<int> adjacentSquares = squaresFromLine(i);
00057             for(int j=0; j<adjacentSquares.size(); j++)
00058             {
00059                 
00060                 if(countBorderLines(adjacentSquares.at(j), lines) == 3) //if 3 lines, draw there to get points!
00061                 {
00062                     choiceList.append(i);
00063                     //kDebug() << "AI: 1. Adding" << i << "to choices";
00064                 }
00065             }
00066         }
00067     }
00068     if(choiceList.size() != 0)
00069     {
00070         float randomFloat = ((float) rand()/(RAND_MAX + 1.0))*(choiceList.size()-1);
00071         int randChoice = (short)(randomFloat)/1;
00072         kDebug() << "AI: 1. Drawing line at index:" << choiceList.at(randChoice);
00073         return choiceList.at(randChoice);
00074     }
00075     
00076     choiceList = safeMoves();
00077     
00078     if(choiceList.size() != 0)
00079     {
00080         float randomFloat = ((float) rand()/(RAND_MAX + 1.0))*(choiceList.size()-1);
00081         int randChoice = (short)(randomFloat)/1;
00082         kDebug() << "AI: 2. Drawing line at index:" << choiceList.at(randChoice);
00083         return choiceList.at(randChoice);
00084     }
00085     
00086     choiceList.clear();
00087     for(int i=0; i<lines.size(); i++)   //have to take what's left
00088     {
00089         if(!lines.at(i))
00090         {
00091             QList<int> adjacentSquares = squaresFromLine(i);
00092             for(int j=0; j<adjacentSquares.size(); j++)
00093             {
00094                 
00095                 if(countBorderLines(adjacentSquares.at(j), lines) == 2) //if 2 lines (they're all that's left!)
00096                 {
00097                     choiceList.append(i);
00098                     //kDebug() << "AI: 3. Adding" << i << "to choices";
00099                 }
00100             }
00101         }
00102     }
00103     if(Settings::difficulty() == 1) //Hard(2/3) //do some damage control :)
00104     {
00105         QList<int> goodChoiceList = chooseLeastDamaging(choiceList);
00106         if(goodChoiceList.size() != 0)
00107         {
00108             float randomFloat = ((float) rand()/(RAND_MAX + 1.0))*(goodChoiceList.size()-1);
00109             int randChoice = (short)(randomFloat)/1;
00110             kDebug() << "AI: 3. Drawing line at index:" << goodChoiceList.at(randChoice);
00111             return goodChoiceList.at(randChoice);
00112         }
00113     }
00114     QList<int> goodcChoiceList = chooseLeastDamaging(choiceList);
00115     if(choiceList.size() != 0)
00116     {
00117         float randomFloat = ((float) rand()/(RAND_MAX + 1.0))*(choiceList.size()-1);
00118         int randChoice = (short)(randomFloat)/1;
00119         kDebug() << "AI: 3. Drawing line at index:" << choiceList.at(randChoice);
00120         return choiceList.at(randChoice);
00121     }
00122         return 0;
00123 }
00124 
00125 QList<int> aiController::safeMoves() const
00126 {
00127     QList<int> safeLines;
00128     for(int i=0; i<lines.size(); i++)   //finding totally safe moves. avoiding squares with 2 lines
00129     {
00130         if(!lines.at(i))
00131         {
00132             QList<int> adjacentSquares = squaresFromLine(i);
00133             int badCount = 0;
00134             for(int j=0; j<adjacentSquares.size(); j++)
00135             {
00136                 if(countBorderLines(adjacentSquares.at(j), lines) == 2) //don't want to make 3 lines around a square
00137                 {
00138                     badCount++;
00139                 }
00140             }
00141             if(badCount == 0)
00142             {
00143                 safeLines.append(i);
00144                 //kDebug() << "AI: 2. Adding" << i << "to choices";
00145             }
00146         }
00147     }
00148     return safeLines;
00149 }
00150 
00151 QList<int> aiController::chooseLeastDamaging(const QList<int> &choiceList) const
00152 {
00153     //kDebug() << "AI: Checking" << choiceList.size() << "possible moves";
00154     QMap<int,int> linePointDamage;  //this will be a list of how damaging a certain move will be. Key = damage of move, Value = index of line
00155     for(int i=0; i<choiceList.size(); i++)  //cycle through all the possible moves
00156     {
00157         QList<int> squaresCopy = squareOwners;  //make temporary local copies of lists
00158         QList<bool> linesCopy = lines;      //make temporary local copies of lists
00159         linesCopy[choiceList.at(i)] = true; //we're going to try drawing a line here
00160         
00161         //now it would be the next player's turn so we're going to count how many squares they would be able to get.
00162         int count = 0;  //this is how many points the next player will ge if you draw a line at choiceList.at(i)
00163         bool squareFound = false;
00164         do
00165         {
00166             for(int currentSquare=0; currentSquare<squaresCopy.size(); currentSquare++) //cycle through the grid (by squares):
00167             {
00168                 if(countBorderLines(currentSquare, linesCopy) == 3) //if we've found a square with three sides drawn:
00169                 {
00170                     count++;
00171                     squareFound = true; //we found a square so we will look again for the next
00172                     
00173                     QList<int> sidesOfSquare = linesFromSquare(currentSquare);
00174                     for(int sideOfSquare=0; sideOfSquare<=3; sideOfSquare++)    //make the square complete in linesCopy
00175                     {
00176                         linesCopy[sidesOfSquare.at(sideOfSquare)] = true;   //draw at this squares
00177                         
00178                     }   //now this square is completed by the second player.
00179                     break;  //since we found a square with 3 sides completed (now = 4), we break the 'for(currentSquare)' loop
00180                 }
00181                 else
00182                 {
00183                     squareFound = false;    //we couldn't find a square this time round so we'll try the next 'i'
00184                 }
00185             }
00186         } while(squareFound == true);   //while we're still finding squares
00187         linePointDamage.insertMulti(count, choiceList.at(i));   //insert a pair with Key=count, Value=i
00188     }
00189     
00190     QList<int> bestMoves = linePointDamage.values(linePointDamage.begin().key());   //this is a list of the indices of the lines that are the least damaging. linePointDamage.begin() returns the 1st pair in the QMap, sorted in ascending order by Key (damage of move)
00191     return bestMoves;
00192 }
00193 
00194 int aiController::countBorderLines(int squareIndex, const QList<bool> &linesList) const
00195 {
00196     int count = 0;
00197     
00198     QList<int> tempLineList = linesFromSquare(squareIndex);
00199     
00200     //TODO: replace this with a QList 'count' type function?
00201     if(linesList.at(tempLineList.at(0)) == true)
00202         count++;
00203     if(linesList.at(tempLineList.at(1)) == true)
00204         count++;
00205     if(linesList.at(tempLineList.at(2)) == true)
00206         count++;
00207     if(linesList.at(tempLineList.at(3)) == true)
00208         count++;
00209     //kDebug() << "AI: Square" << squareIndex << "is bordered by" << count << "lines";
00210     return count;
00211 }
00212 
00213 QList<int> aiController::squaresFromLine(int lineIndex) const
00214 {
00215     //kDebug() << "Line:" << lineIndex;
00216     QList<int> squareList;
00217     if (lineDirection(lineIndex) == KSquares::HORIZONTAL)
00218     {
00219         squareList.append(lineIndex - ( (width+1) * (lineIndex/((width*2)+1)) ));
00220         squareList.append(squareList.at(0) - width);
00221         if(squareList.at(1) < 0)
00222             squareList.removeAt(1);
00223         if(squareList.at(0) >= (width*height))
00224             squareList.removeAt(0);
00225             
00226     }
00227     else if (lineDirection(lineIndex) == KSquares::VERTICAL)
00228     {
00229         squareList.append(lineIndex - ( (lineIndex/((width*2)+1))*width + (lineIndex/((width*2)+1)) + width ));
00230         squareList.append(squareList.at(0) - 1);
00231         if(lineIndex%((2*width)+1) == width)
00232             squareList.removeAt(1);
00233         if(lineIndex%((2*width)+1) == 2*width)
00234             squareList.removeAt(0);
00235     }
00236     //kDebug() << "Size:" << squareList.size();
00237     //kDebug() << "squares:" << squareList.at(0) << " " << squareList.at(1);
00238     return squareList;
00239 }
00240 
00241 QList<int> aiController::linesFromSquare(int squareIndex) const
00242 {
00243     QList<int> tempLineList;
00244     int index1 = (squareIndex/width) * ((2*width) + 1) + (squareIndex%width);
00245     int index2 = index1 + width;
00246     int index3 = index2 + 1;
00247     int index4 = index3 + width;
00248     tempLineList.append(index1);
00249     tempLineList.append(index2);
00250     tempLineList.append(index3);
00251     tempLineList.append(index4);
00252     return tempLineList;
00253 }
00254 
00255 KSquares::Direction aiController::lineDirection(int lineIndex) const
00256 {
00257     int index2 = lineIndex % ((2*width) + 1);
00258     KSquares::Direction dir;
00259     if(index2 < width)
00260         dir = KSquares::HORIZONTAL;
00261     else
00262         dir = KSquares::VERTICAL;
00263     
00264     return dir;
00265 }

ksquares

Skip menu "ksquares"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • kblackbox
  • kgoldrunner
  • kmahjongg
  • ksquares
  • libkdegames
  •   highscore
  •   kgame
  •   kggzgames
  •   kggzmod
  •   kggznet
  • libkmahjongg
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal