board.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 1999-2005 Eliot
00003  * Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
00004  *          Olivier Teuliere  <ipkiss@via.ecp.fr>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  *****************************************************************************/
00020 
00021 #include "dic.h"
00022 #include "tile.h"
00023 #include "round.h"
00024 #include "bag.h"
00025 #include "rack.h"
00026 #include "results.h"
00027 #include "board.h"
00028 
00029 #define oo 0
00030 #define __ 1
00031 #define T2 2
00032 #define T3 3
00033 #define W2 2
00034 #define W3 3
00035 
00036 
00037 const int Board::m_tileMultipliers[BOARD_REALDIM][BOARD_REALDIM] =
00038 {
00039     { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
00040     { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
00041     { oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
00042     { oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
00043     { oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
00044     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00045     { oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
00046     { oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
00047     { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
00048     { oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
00049     { oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
00050     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00051     { oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
00052     { oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
00053     { oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
00054     { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
00055     { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
00056 };
00057 
00058 
00059 const int Board::m_wordMultipliers[BOARD_REALDIM][BOARD_REALDIM] =
00060 {
00061     { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
00062     { oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
00063     { oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
00064     { oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
00065     { oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
00066     { oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
00067     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00068     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00069     { oo,W3,__,__,__,__,__,__,W2,__,__,__,__,__,__,W3,oo },
00070     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00071     { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
00072     { oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
00073     { oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
00074     { oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
00075     { oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
00076     { oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
00077     { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
00078 };
00079 
00080 
00081 Board::Board():
00082     m_tilesRow(BOARD_REALDIM, Tile::dummy()),
00083     m_tilesCol(BOARD_REALDIM, Tile::dummy()),
00084     m_jokerRow(BOARD_REALDIM, false),
00085     m_jokerCol(BOARD_REALDIM, false),
00086     m_crossRow(BOARD_REALDIM, Cross()),
00087     m_crossCol(BOARD_REALDIM, Cross()),
00088     m_pointRow(BOARD_REALDIM, -1),
00089     m_pointCol(BOARD_REALDIM, -1),
00090     m_testsRow(BOARD_REALDIM, 0)
00091 {
00092     // No cross check allowed around the board
00093     for (int i = 0; i < BOARD_REALDIM; i++)
00094     {
00095         m_crossRow[0][i].clear();
00096         m_crossCol[0][i].clear();
00097         m_crossRow[i][0].clear();
00098         m_crossCol[i][0].clear();
00099         m_crossRow[BOARD_REALDIM - 1][i].clear();
00100         m_crossCol[BOARD_REALDIM - 1][i].clear();
00101         m_crossRow[i][BOARD_REALDIM - 1].clear();
00102         m_crossCol[i][BOARD_REALDIM - 1].clear();
00103     }
00104 }
00105 
00106 
00107 char Board::getChar(int iRow, int iCol) const
00108 {
00109     char letter = 0;
00110     Tile tile = getTile(iRow, iCol);
00111     if (!tile.isEmpty())
00112     {
00113         letter = tile.toChar();
00114         if (isJoker(iRow, iCol))
00115             letter = tolower(letter);
00116     }
00117     return letter;
00118 }
00119 
00120 int Board::getCharAttr(int iRow, int iCol) const
00121 {
00122     int t = getTestChar(iRow, iCol);
00123     int j = isJoker(iRow, iCol);
00124     return  (t << 1) | j;
00125 }
00126 
00127 
00128 Tile Board::getTile(int iRow, int iCol) const
00129 {
00130     return m_tilesRow[iRow][iCol];
00131 }
00132 
00133 
00134 bool Board::isJoker(int iRow, int iCol) const
00135 {
00136     return m_jokerRow[iRow][iCol];
00137 }
00138 
00139 
00140 bool Board::isVacant(int iRow, int iCol) const
00141 {
00142     if (iRow < 1 || iRow > BOARD_DIM ||
00143         iCol < 1 || iCol > BOARD_DIM)
00144     {
00145         return false;
00146     }
00147     return m_tilesRow[iRow][iCol].isEmpty();
00148 }
00149 
00150 
00151 void Board::addRound(const Dictionary &iDic, const Round &iRound)
00152 {
00153     Tile t;
00154     int row, col;
00155 
00156     row = iRound.getCoord().getRow();
00157     col = iRound.getCoord().getCol();
00158     if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
00159     {
00160         for (int i = 0; i < iRound.getWordLen(); i++)
00161         {
00162             if (m_tilesRow[row][col + i].isEmpty())
00163             {
00164                 t = iRound.getTile(i);
00165                 m_tilesRow[row][col + i] = t;
00166                 m_jokerRow[row][col + i] = iRound.isJoker(i);
00167                 m_tilesCol[col + i][row] = t;
00168                 m_jokerCol[col + i][row] = iRound.isJoker(i);
00169             }
00170         }
00171     }
00172     else
00173     {
00174         for (int i = 0; i < iRound.getWordLen(); i++)
00175         {
00176             if (m_tilesRow[row + i][col].isEmpty())
00177             {
00178                 t = iRound.getTile(i);
00179                 m_tilesRow[row + i][col] = t;
00180                 m_jokerRow[row + i][col] = iRound.isJoker(i);
00181                 m_tilesCol[col][row + i] = t;
00182                 m_jokerCol[col][row + i] = iRound.isJoker(i);
00183             }
00184         }
00185     }
00186     buildCross(iDic);
00187 #ifdef DEBUG
00188     checkDouble();
00189 #endif
00190 }
00191 
00192 
00193 void Board::removeRound(const Dictionary &iDic, const Round &iRound)
00194 {
00195     int row, col;
00196 
00197     row = iRound.getCoord().getRow();
00198     col = iRound.getCoord().getCol();
00199     if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
00200     {
00201         for (int i = 0; i < iRound.getWordLen(); i++)
00202         {
00203             if (iRound.isPlayedFromRack(i))
00204             {
00205                 m_tilesRow[row][col + i] = Tile::dummy();
00206                 m_jokerRow[row][col + i] = false;
00207                 m_tilesCol[col + i][row] = Tile::dummy();
00208                 m_jokerCol[col + i][row] = false;
00209             }
00210         }
00211     }
00212     else
00213     {
00214         for (int i = 0; i < iRound.getWordLen(); i++)
00215         {
00216             if (iRound.isPlayedFromRack(i))
00217             {
00218                 m_tilesRow[row + i][col] = Tile::dummy();
00219                 m_jokerRow[row + i][col] = false;
00220                 m_tilesCol[col][row + i] = Tile::dummy();
00221                 m_jokerCol[col][row + i] = false;
00222             }
00223         }
00224     }
00225     buildCross(iDic);
00226 #ifdef DEBUG
00227     checkDouble();
00228 #endif
00229 }
00230 
00231 
00232 /* XXX: There is duplicated code with board_search.c.
00233  * We could probably factorize something... */
00234 int Board::checkRoundAux(Matrix<Tile> &iTilesMx,
00235                          Matrix<Cross> &iCrossMx,
00236                          Matrix<int> &iPointsMx,
00237                          Matrix<bool> &iJokerMx,
00238                          Round &iRound,
00239                          bool firstturn)
00240 {
00241     Tile t;
00242     int row, col, i;
00243     int l, p, fromrack;
00244     int pts, ptscross, wordmul;
00245     bool isolated = true;
00246 
00247     fromrack = 0;
00248     pts = 0;
00249     ptscross = 0;
00250     wordmul = 1;
00251     row = iRound.getCoord().getRow();
00252     col = iRound.getCoord().getCol();
00253 
00254     /* Is the word an extension of another word? */
00255     if (!iTilesMx[row][col - 1].isEmpty() ||
00256         !iTilesMx[row][col + iRound.getWordLen()].isEmpty())
00257     {
00258         return 1;
00259     }
00260 
00261     for (i = 0; i < iRound.getWordLen(); i++)
00262     {
00263         t = iRound.getTile(i);
00264         if (!iTilesMx[row][col + i].isEmpty())
00265         {
00266             /* There is already a letter on the board */
00267             if (iTilesMx[row][col + i] != t)
00268                 return 2;
00269 
00270             isolated = false;
00271             iRound.setFromBoard(i);
00272 
00273             if (!iJokerMx[row][col + i])
00274                 pts += t.getPoints();
00275         }
00276         else
00277         {
00278             /* The letter is not yet on the board */
00279             if (iCrossMx[row][col + i].check(t))
00280             {
00281                 /* A non-trivial cross-check means an anchor square */
00282                 if (!iCrossMx[row][col + i].isAny())
00283                     isolated = false;
00284 
00285                 if (!iRound.isJoker(i))
00286                     l = t.getPoints() * m_tileMultipliers[row][col + i];
00287                 else
00288                     l = 0;
00289                 pts += l;
00290                 wordmul *= m_wordMultipliers[row][col + i];
00291 
00292                 p = iPointsMx[row][col + i];
00293                 if (p >= 0)
00294                 {
00295                     ptscross += (p + l) * m_wordMultipliers[row][col + i];
00296                 }
00297                 fromrack++;
00298                 iRound.setFromRack(i);
00299             }
00300             else
00301             {
00302                 /* The letter is not in the crosscheck */
00303                 return 3;
00304             }
00305         }
00306     }
00307 
00308     /* There must be at least 1 letter from the rack */
00309     if (fromrack == 0)
00310         return 4;
00311 
00312     /* The word must cover at least one anchor square, except
00313      * for the first turn */
00314     if (isolated && !firstturn)
00315         return 5;
00316     /* The first word must be horizontal */
00317     if (firstturn && iRound.getCoord().getDir() == Coord::VERTICAL)
00318         return 6;
00319     /* The first word must cover the H8 square */
00320     if (firstturn
00321         && (row != 8 || col > 8 || col + iRound.getWordLen() <= 8))
00322     {
00323         return 7;
00324     }
00325 
00326     /* Set the iPointsMx and bonus */
00327     pts = ptscross + pts * wordmul + 50 * (fromrack == 7);
00328     iRound.setPoints(pts);
00329     iRound.setBonus(fromrack == 7);
00330 
00331     return 0;
00332 }
00333 
00334 
00335 int Board::checkRound(Round &iRound, bool firstturn)
00336 {
00337     if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
00338     {
00339         return checkRoundAux(m_tilesRow, m_crossRow,
00340                              m_pointRow, m_jokerRow,
00341                              iRound, firstturn);
00342     }
00343     else
00344     {
00345         // XXX: ugly!
00346         // Exchange the coordinates temporarily
00347         iRound.accessCoord().swap();
00348 
00349         int res = checkRoundAux(m_tilesCol, m_crossCol,
00350                                 m_pointCol, m_jokerCol,
00351                                 iRound, firstturn);
00352 
00353         // Restore the coordinates
00354         iRound.accessCoord().swap();
00355 
00356         return res;
00357     }
00358 }
00359 
00360 
00361 void Board::testRound(const Round &iRound)
00362 {
00363     Tile t;
00364     int row, col;
00365 
00366     row = iRound.getCoord().getRow();
00367     col = iRound.getCoord().getCol();
00368     if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
00369     {
00370         for (int i = 0; i < iRound.getWordLen(); i++)
00371         {
00372             if (m_tilesRow[row][col + i].isEmpty())
00373             {
00374                 t = iRound.getTile(i);
00375                 m_tilesRow[row][col + i] = t;
00376                 m_jokerRow[row][col + i] = iRound.isJoker(i);
00377                 m_testsRow[row][col + i] = 1;
00378 
00379                 m_tilesCol[col + i][row] = t;
00380                 m_jokerCol[col + i][row] = iRound.isJoker(i);
00381             }
00382         }
00383     }
00384     else
00385     {
00386         for (int i = 0; i < iRound.getWordLen(); i++)
00387         {
00388             if (m_tilesRow[row + i][col].isEmpty())
00389             {
00390                 t = iRound.getTile(i);
00391                 m_tilesRow[row + i][col] = t;
00392                 m_jokerRow[row + i][col] = iRound.isJoker(i);
00393                 m_testsRow[row + i][col] = 1;
00394 
00395                 m_tilesCol[col][row + i] = t;
00396                 m_jokerCol[col][row + i] = iRound.isJoker(i);
00397             }
00398         }
00399     }
00400 }
00401 
00402 
00403 void Board::removeTestRound()
00404 {
00405     for (int row = 1; row <= BOARD_DIM; row++)
00406     {
00407         for (int col = 1; col <= BOARD_DIM; col++)
00408         {
00409             if (m_testsRow[row][col])
00410             {
00411                 m_tilesRow[row][col] = Tile::dummy();
00412                 m_testsRow[row][col] = 0;
00413                 m_jokerRow[row][col] = false;
00414 
00415                 m_tilesCol[col][row] = Tile::dummy();
00416                 m_jokerCol[col][row] = false;
00417             }
00418         }
00419     }
00420 }
00421 
00422 
00423 char Board::getTestChar(int iRow, int iCol) const
00424 {
00425     return m_testsRow[iRow][iCol];
00426 }
00427 
00428 
00429 int Board::getWordMultiplier(int iRow, int iCol) const
00430 {
00431     if (iRow < BOARD_MIN || iRow > BOARD_MAX ||
00432         iCol < BOARD_MIN || iCol > BOARD_MAX)
00433         return 0;
00434     return m_wordMultipliers[iRow][iCol];
00435 }
00436 
00437 
00438 int Board::getLetterMultiplier(int iRow, int iCol) const
00439 {
00440     if (iRow < BOARD_MIN || iRow > BOARD_MAX ||
00441         iCol < BOARD_MIN || iCol > BOARD_MAX)
00442         return 0;
00443     return m_tileMultipliers[iRow][iCol];
00444 }
00445 
00446 
00447 #ifdef DEBUG
00448 void Board::checkDouble()
00449 {
00450     for (int row = BOARD_MIN; row <= BOARD_MAX; row++)
00451     {
00452         for (int col = BOARD_MIN; col <= BOARD_MAX; col++)
00453         {
00454             if (m_tilesRow[row][col] != m_tilesCol[col][row])
00455                 printf("tiles diff %d %d\n", row, col);
00456 
00457             // The crossckecks and the points have no reason to be the same
00458             // in both directions
00459             /*
00460             if (m_crossRow[row][col] != m_crossCol[col][row])
00461             {
00462                 printf("cross diff %d %d\n",row,col);
00463             }
00464 
00465             if (m_pointRow[row][col] != m_pointCol[col][row])
00466                 printf("point diff %d %d\n",row,col);
00467             */
00468 
00469             if (m_jokerRow[row][col] != m_jokerCol[col][row])
00470                 printf("joker diff %d %d\n", row, col);
00471         }
00472     }
00473 }
00474 #endif
00475 
00476 /// Local Variables:
00477 /// mode: c++
00478 /// mode: hs-minor
00479 /// c-basic-offset: 4
00480 /// indent-tabs-mode: nil
00481 /// End:

Generated on Thu Dec 29 02:01:14 2005 for Eliot by  doxygen 1.4.5