Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoUctPatterns.h

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoUctPatterns.h
00003 */
00004 //----------------------------------------------------------------------------
00005 
00006 #ifndef GOUCT_PATTERNS_H
00007 #define GOUCT_PATTERNS_H
00008 
00009 #include "GoBoard.h"
00010 #include "GoBoardUtil.h"
00011 #include "SgBoardColor.h"
00012 #include "SgBWArray.h"
00013 #include "SgPoint.h"
00014 
00015 //----------------------------------------------------------------------------
00016 
00017 /** Some hard-coded pattern matching routines to match patterns used by MoGo.
00018     See <a href="http://hal.inria.fr/docs/00/11/72/66/PDF/MoGoReport.pdf">
00019     Modification of UCT with Patterns in Monte-Carlo Go</a>.
00020 
00021     The move is always in the center of the pattern or at the middle edge
00022     point (lower line) for edge patterns. The patterns are matched for both
00023     colors, unless specified otherwise. Notation:
00024     @verbatim
00025     O  White            x = Black or Empty
00026     X = Black           o = White or Empty
00027     . = Empty           B = Black to Play
00028     ? = Don't care      W = White to Play
00029     @endverbatim
00030 
00031     Patterns for Hane. <br>
00032     True is returned if any pattern is matched.
00033     @verbatim
00034     X O X   X O .   X O ?   X O O
00035     . . .   . . .   X . .   . . .
00036     ? ? ?   ? . ?   ? . ?   ? . ? B
00037     @endverbatim
00038 
00039     Patterns for Cut1. <br>
00040     True is returned if the first pattern is matched, but not the next two.
00041     @verbatim
00042     X O ?   X O ?   X O ?
00043     O . ?   O . O   O . .
00044     ? ? ?   ? . ?   ? O ?
00045     @endverbatim
00046 
00047     Pattern for Cut2.
00048     @verbatim
00049     ? X ?
00050     O . O
00051     x x x
00052     @endverbatim
00053 
00054     Pattern for Edge. <br>
00055     True is returned if any pattern is matched.
00056     @verbatim
00057     X . ?   ? X ?   ? X O    ? X O    ? X O
00058     O . ?   o . O   ? . ? B  ? . o W  O . X W
00059     @endverbatim
00060 */
00061 template<class BOARD>
00062 class GoUctPatterns
00063 {
00064 public:
00065     GoUctPatterns(const BOARD& bd);
00066 
00067     /** Match any of the patterns. */
00068     bool MatchAny(SgPoint p) const;
00069 
00070 private:
00071     /** 3^5 = size of edge pattern table */
00072     static const int GOUCT_POWER3_5 = 3 * 3 * 3 * 3 * 3;
00073 
00074     /** 3^8 = size of center pattern table. */
00075     static const int GOUCT_POWER3_8 = 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3;
00076 
00077     /** See m_edgeTable. */
00078     typedef SgArray<bool, GOUCT_POWER3_5> GoUctEdgePatternTable;
00079 
00080     /** See m_table. */
00081     typedef SgArray<bool, GOUCT_POWER3_8> GoUctPatternTable;
00082 
00083     const BOARD& m_bd;
00084 
00085     /** lookup table for 8-neighborhood of a move candidate */
00086     SgBWArray<GoUctPatternTable> m_table;
00087 
00088     /** lookup table on the edge of board */
00089     SgBWArray<GoUctEdgePatternTable> m_edgeTable;
00090 
00091     static bool CheckCut1(const GoBoard& bd, SgPoint p, SgBlackWhite c,
00092                           int cDir, int otherDir);
00093 
00094     static bool CheckCut2(const GoBoard& bd, SgPoint p, const SgBlackWhite c,
00095                           int cDir, int otherDir);
00096 
00097     static bool CheckHane1(const GoBoard& bd, SgPoint p, SgBlackWhite c,
00098                            SgBlackWhite opp, int cDir, int otherDir);
00099 
00100     static int CodeOf8Neighbors(const BOARD& bd, SgPoint p);
00101 
00102     static int CodeOfEdgeNeighbors(const BOARD& bd, SgPoint p);
00103 
00104     static int EdgeDirection(GoBoard& bd, SgPoint p, int index);
00105 
00106     static int EBWCodeOfPoint(const BOARD& bd, SgPoint p);
00107 
00108     static int FindDir(const GoBoard& bd, SgPoint p, SgBlackWhite c);
00109 
00110     static void InitCenterPatternTable(SgBWArray<GoUctPatternTable>& table);
00111 
00112     static void InitEdgePatternTable(SgBWArray<GoUctEdgePatternTable>&
00113                                      edgeTable);
00114 
00115     static bool MatchCut(const GoBoard& bd, SgPoint p);
00116 
00117     static bool MatchEdge(const GoBoard& bd, SgPoint p, const int nuBlack,
00118                           const int nuWhite);
00119 
00120     static bool MatchHane(const GoBoard& bd, SgPoint p, const int nuBlack,
00121                           const int nuWhite);
00122 
00123     static bool MatchAnyPattern(const GoBoard& bd, SgPoint p);
00124 
00125     static int OtherDir(int dir);
00126 
00127     static int SetupCodedEdgePosition(GoBoard& bd, int code);
00128 
00129     static int SetupCodedPosition(GoBoard& bd, int code);
00130 
00131     /** Match any of the center patterns. */
00132     bool MatchAnyCenter(SgPoint p) const;
00133 
00134     /** Match any of the edge patterns. */
00135     bool MatchAnyEdge(SgPoint p) const;
00136 };
00137 
00138 template<class BOARD>
00139 GoUctPatterns<BOARD>::GoUctPatterns(const BOARD& bd)
00140     : m_bd(bd)
00141 {
00142     InitCenterPatternTable(m_table);
00143     InitEdgePatternTable(m_edgeTable);
00144 }
00145 
00146 template<class BOARD>
00147 bool GoUctPatterns<BOARD>::CheckHane1(const GoBoard& bd, SgPoint p,
00148                                       SgBlackWhite c, SgBlackWhite opp,
00149                                       int cDir, int otherDir)
00150 {
00151     return    bd.IsColor(p + cDir, c)
00152            && bd.IsColor(p + cDir + otherDir, opp)
00153            && bd.IsColor(p + cDir - otherDir, opp)
00154            && bd.IsEmpty(p + otherDir)
00155            && bd.IsEmpty(p - otherDir)
00156            ;
00157 }
00158 
00159 template<class BOARD>
00160 bool GoUctPatterns<BOARD>::CheckCut1(const GoBoard& bd, SgPoint p,
00161                                      SgBlackWhite c, int cDir, int otherDir)
00162 {
00163     SG_ASSERT_BW(c);
00164     return bd.IsColor(p + otherDir, c)
00165         && bd.IsColor(p + cDir + otherDir, SgOppBW(c));
00166 }
00167 
00168 template<class BOARD>
00169 bool GoUctPatterns<BOARD>::CheckCut2(const GoBoard& bd, SgPoint p,
00170                                      const SgBlackWhite c, int cDir,
00171                                      int otherDir)
00172 {
00173     SG_ASSERT_BW(c);
00174     SG_ASSERT(bd.IsColor(p + cDir, c));
00175     const SgBlackWhite opp = SgOppBW(c);
00176     return   bd.IsColor(p - cDir, c)
00177           && ( (  bd.IsColor(p + otherDir, opp)
00178                && ! bd.IsColor(p - otherDir + cDir, c)
00179                && ! bd.IsColor(p - otherDir - cDir, c)
00180                )
00181              ||
00182                (  bd.IsColor(p - otherDir, opp)
00183                && ! bd.IsColor(p + otherDir + cDir, c)
00184                && ! bd.IsColor(p + otherDir - cDir, c)
00185                )
00186              );
00187 }
00188 
00189 template<class BOARD>
00190 inline int GoUctPatterns<BOARD>::CodeOf8Neighbors(const BOARD& bd, SgPoint p)
00191 {
00192     SG_ASSERT(bd.Line(p) > 1);
00193     int code = (((((( EBWCodeOfPoint(bd, p - SG_NS - SG_WE) * 3
00194                     + EBWCodeOfPoint(bd, p - SG_NS)) * 3
00195                     + EBWCodeOfPoint(bd, p - SG_NS + SG_WE)) * 3
00196                     + EBWCodeOfPoint(bd, p - SG_WE)) * 3
00197                     + EBWCodeOfPoint(bd, p + SG_WE)) * 3
00198                     + EBWCodeOfPoint(bd, p + SG_NS - SG_WE)) * 3
00199                     + EBWCodeOfPoint(bd, p + SG_NS)) * 3
00200                     + EBWCodeOfPoint(bd, p + SG_NS + SG_WE);
00201     SG_ASSERT(code >= 0);
00202     SG_ASSERT(code < GOUCT_POWER3_8);
00203     return code;
00204 }
00205 
00206 template<class BOARD>
00207 inline int GoUctPatterns<BOARD>::CodeOfEdgeNeighbors(const BOARD& bd,
00208                                                      SgPoint p)
00209 {
00210     SG_ASSERT(bd.Line(p) == 1);
00211     SG_ASSERT(bd.Pos(p) > 1);
00212     const int up = bd.Up(p);
00213     const int other = OtherDir(up);
00214     int code = (((EBWCodeOfPoint(bd, p + other) * 3
00215                 + EBWCodeOfPoint(bd, p + up + other)) * 3
00216                 + EBWCodeOfPoint(bd, p + up)) * 3
00217                 + EBWCodeOfPoint(bd, p + up - other)) * 3
00218                 + EBWCodeOfPoint(bd, p - other);
00219     SG_ASSERT(code >= 0);
00220     SG_ASSERT(code < GOUCT_POWER3_5);
00221     return code;
00222 }
00223 
00224 template<class BOARD>
00225 inline int GoUctPatterns<BOARD>::EBWCodeOfPoint(const BOARD& bd, SgPoint p)
00226 {
00227     SG_ASSERT(bd.IsValidPoint(p));
00228     BOOST_STATIC_ASSERT(SG_BLACK == 0);
00229     BOOST_STATIC_ASSERT(SG_WHITE == 1);
00230     BOOST_STATIC_ASSERT(SG_EMPTY == 2);
00231     return bd.GetColor(p);
00232 }
00233 
00234 template<class BOARD>
00235 int GoUctPatterns<BOARD>::EdgeDirection(GoBoard& bd, SgPoint p, int index)
00236 {
00237     const int up = bd.Up(p);
00238     switch (index)
00239     {
00240         case 0:
00241             return OtherDir(up);
00242         case 1:
00243             return up + OtherDir(up);
00244         case 2:
00245             return up;
00246         case 3:
00247             return up - OtherDir(up);
00248         default:
00249             SG_ASSERT(index == 4);
00250             return - OtherDir(up);
00251     }
00252 }
00253 
00254 /** Find direction of a neighboring stone in color c */
00255 template<class BOARD>
00256 int GoUctPatterns<BOARD>::FindDir(const GoBoard& bd, SgPoint p,
00257                                   SgBlackWhite c)
00258 {
00259     if (bd.IsColor(p + SG_NS, c))
00260         return SG_NS;
00261     if (bd.IsColor(p - SG_NS, c))
00262         return -SG_NS;
00263     if (bd.IsColor(p + SG_WE, c))
00264         return SG_WE;
00265     SG_ASSERT(bd.IsColor(p - SG_WE, c));
00266     return -SG_WE;
00267 }
00268 
00269 template<class BOARD>
00270 void GoUctPatterns<BOARD>::InitEdgePatternTable(
00271                                   SgBWArray<GoUctEdgePatternTable>& edgeTable)
00272 {
00273     GoBoard bd(5);
00274     const SgPoint p = SgPointUtil::Pt(1, 3);
00275     for (int i = 0; i < GOUCT_POWER3_5; ++i)
00276     {
00277         int count = SetupCodedEdgePosition(bd, i);
00278         for(SgBWIterator it; it; ++it)
00279         {
00280             bd.SetToPlay(*it);
00281             edgeTable[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0;
00282         }
00283         while (count-- > 0)
00284             bd.Undo();
00285     }
00286 }
00287 
00288 template<class BOARD>
00289 void GoUctPatterns<BOARD>::InitCenterPatternTable(
00290                                           SgBWArray<GoUctPatternTable>& table)
00291 {
00292     GoBoard bd(5);
00293     const SgPoint p = SgPointUtil::Pt(3, 3);
00294     for (int i = 0; i < GOUCT_POWER3_8; ++i)
00295     {
00296         int count = SetupCodedPosition(bd, i);
00297         for(SgBWIterator it; it; ++it)
00298         {
00299             bd.SetToPlay(*it);
00300             table[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0;
00301         }
00302         while (count-- > 0)
00303             bd.Undo();
00304     }
00305 }
00306 
00307 template<class BOARD>
00308 bool GoUctPatterns<BOARD>::MatchCut(const GoBoard& bd, SgPoint p)
00309 {
00310     if (bd.Num8EmptyNeighbors(p) > 6)
00311         return false;
00312 
00313     const int nuEmpty = bd.NumEmptyNeighbors(p);
00314     //cut1
00315     const SgEmptyBlackWhite c1 = bd.GetColor(p + SG_NS);
00316     if (   c1 != SG_EMPTY
00317        && bd.NumNeighbors(p, c1) >= 2
00318        && ! (bd.NumNeighbors(p, c1) == 3 && nuEmpty == 1)
00319        && (  CheckCut1(bd, p, c1, SG_NS, SG_WE)
00320           || CheckCut1(bd, p, c1, SG_NS, -SG_WE)
00321           )
00322        )
00323         return true;
00324     const SgEmptyBlackWhite c2 = bd.GetColor(p - SG_NS);
00325     if (  c2 != SG_EMPTY
00326        && bd.NumNeighbors(p, c2) >= 2
00327        && ! (bd.NumNeighbors(p, c2) == 3 && nuEmpty == 1)
00328        && (  CheckCut1(bd, p, c2, -SG_NS, SG_WE)
00329           || CheckCut1(bd, p, c2, -SG_NS, -SG_WE)
00330           )
00331        )
00332         return true;
00333     //cut2
00334     if (  c1 != SG_EMPTY
00335        && bd.NumNeighbors(p, c1) == 2
00336        && bd.NumNeighbors(p, SgOppBW(c1)) > 0
00337        && bd.NumDiagonals(p, c1) <= 2
00338        && CheckCut2(bd, p, c1, SG_NS, SG_WE)
00339        )
00340         return true;
00341     const SgEmptyBlackWhite c3 = bd.GetColor(p + SG_WE);
00342     if (  c3 != SG_EMPTY
00343        && bd.NumNeighbors(p, c3) == 2
00344        && bd.NumNeighbors(p, SgOppBW(c3)) > 0
00345        && bd.NumDiagonals(p, c3) <= 2
00346        && CheckCut2(bd, p, c3, SG_WE, SG_NS)
00347        )
00348         return true;
00349     return false;
00350 }
00351 
00352 template<class BOARD>
00353 bool GoUctPatterns<BOARD>::MatchEdge(const GoBoard& bd, SgPoint p,
00354                                      const int nuBlack, const int nuWhite)
00355 {
00356     const int up = bd.Up(p);
00357     const int side = OtherDir(up);
00358     const int nuEmpty = bd.NumEmptyNeighbors(p);
00359     const SgEmptyBlackWhite upColor = bd.GetColor(p + up);
00360     // edge1
00361     if (  nuEmpty > 0
00362        && (nuBlack > 0 || nuWhite > 0)
00363        && upColor == SG_EMPTY
00364        )
00365     {
00366         const SgEmptyBlackWhite c1 = bd.GetColor(p + side);
00367         if (c1 != SG_EMPTY && bd.GetColor(p + side + up) == SgOppBW(c1))
00368             return true;
00369         const SgEmptyBlackWhite c2 = bd.GetColor(p - side);
00370         if (c2 != SG_EMPTY && bd.GetColor(p - side + up) == SgOppBW(c2))
00371             return true;
00372     }
00373 
00374     // edge2
00375     if (  upColor != SG_EMPTY
00376        && (  (upColor == SG_BLACK && nuBlack == 1 && nuWhite > 0)
00377           || (upColor == SG_WHITE && nuWhite == 1 && nuBlack > 0)
00378           )
00379        )
00380         return true;
00381 
00382     const SgBlackWhite toPlay = bd.ToPlay();
00383     // edge3
00384     if (  upColor == toPlay
00385        && bd.NumDiagonals(p, SgOppBW(upColor)) > 0
00386        )
00387         return true;
00388 
00389     // edge4
00390     if (  upColor == SgOppBW(toPlay)
00391        && bd.NumNeighbors(p, upColor) <= 2
00392        && bd.NumDiagonals(p, toPlay) > 0
00393        )
00394     {
00395         if (  bd.GetColor(p + side + up) == toPlay
00396            && bd.GetColor(p + side) != upColor
00397            )
00398             return true;
00399         if (  bd.GetColor(p - side + up) == toPlay
00400            && bd.GetColor(p - side) != upColor
00401            )
00402             return true;
00403     }
00404     // edge5
00405     if (  upColor == SgOppBW(toPlay)
00406        && bd.NumNeighbors(p, upColor) == 2
00407        && bd.NumNeighbors(p, toPlay) == 1
00408        )
00409     {
00410         if (  bd.GetColor(p + side + up) == toPlay
00411            && bd.GetColor(p + side) == upColor
00412            )
00413             return true;
00414         if (  bd.GetColor(p - side + up) == toPlay
00415            && bd.GetColor(p - side) == upColor
00416            )
00417             return true;
00418     }
00419     return false;
00420 }
00421 
00422 template<class BOARD>
00423 bool GoUctPatterns<BOARD>::MatchHane(const GoBoard& bd, SgPoint p,
00424                                      const int nuBlack, const int nuWhite)
00425 {
00426     const int nuEmpty = bd.NumEmptyNeighbors(p);
00427     if (nuEmpty < 2 || nuEmpty > 3)
00428         return false;
00429     if (  (nuBlack < 1 || nuBlack > 2)
00430        && (nuWhite < 1 || nuWhite > 2)
00431        )
00432         return false;
00433     if (nuEmpty == 2) // hane3 pattern
00434     {
00435         if (nuBlack == 1 && nuWhite == 1)
00436         {
00437             const int dirB = FindDir(bd, p, SG_BLACK);
00438             const int dirW = FindDir(bd, p, SG_WHITE);
00439             if (! bd.IsEmpty(p + dirB + dirW))
00440                 return true;
00441         }
00442     }
00443     else if (nuEmpty == 3) // hane2 or hane4
00444     {
00445         SG_ASSERT(nuBlack + nuWhite == 1);
00446         const SgBlackWhite col = (nuBlack == 1) ? SG_BLACK : SG_WHITE;
00447         const SgBlackWhite opp = SgOppBW(col);
00448         const int dir = FindDir(bd, p, col);
00449         const int otherDir = OtherDir(dir);
00450         if (  bd.IsEmpty(p + dir + otherDir)
00451            && bd.IsColor(p + dir - otherDir, opp)
00452            )
00453             return true; // hane2
00454         if (  bd.IsEmpty(p + dir - otherDir)
00455            && bd.IsColor(p + dir + otherDir, opp)
00456            )
00457             return true; // hane2
00458         if (bd.ToPlay() == opp)
00459         {
00460             const SgEmptyBlackWhite c1 = bd.GetColor(p + dir + otherDir);
00461             if (c1 != SG_EMPTY)
00462             {
00463                 const SgEmptyBlackWhite c2 =
00464                 bd.GetColor(p + dir - otherDir);
00465                 if (SgOppBW(c1) == c2)
00466                     return true; // hane4
00467             }
00468         }
00469     }
00470 
00471     // hane1 pattern
00472     const int nuBlackDiag = bd.NumDiagonals(p, SG_BLACK);
00473     if (  nuBlackDiag >= 2
00474        && nuWhite > 0
00475        && (  CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_NS, SG_WE)
00476           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_NS, SG_WE)
00477           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_WE, SG_NS)
00478           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_WE, SG_NS)
00479           )
00480        )
00481         return true;
00482     const int nuWhiteDiag = bd.NumDiagonals(p, SG_WHITE);
00483     if (  nuWhiteDiag >= 2
00484        && nuBlack > 0
00485        && (  CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_NS, SG_WE)
00486           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_NS, SG_WE)
00487           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_WE, SG_NS)
00488           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_WE, SG_NS)
00489           )
00490        )
00491         return true;
00492     return false;
00493 }
00494 
00495 template<class BOARD>
00496 inline bool GoUctPatterns<BOARD>::MatchAnyCenter(SgPoint p) const
00497 {
00498     return m_table[m_bd.ToPlay()][CodeOf8Neighbors(m_bd, p)];
00499 }
00500 
00501 template<class BOARD>
00502 inline bool GoUctPatterns<BOARD>::MatchAnyEdge(SgPoint p) const
00503 {
00504     return m_edgeTable[m_bd.ToPlay()][CodeOfEdgeNeighbors(m_bd, p)];
00505 }
00506 
00507 template<class BOARD>
00508 inline bool GoUctPatterns<BOARD>::MatchAny(SgPoint p) const
00509 {
00510     // Quick refutation using the incremental neighbor counts of the board:
00511     // all patterns have at least one adjacent stone
00512     if (  m_bd.NumNeighbors(p, SG_BLACK) == 0
00513        && m_bd.NumNeighbors(p, SG_WHITE) == 0
00514        )
00515         return false;
00516     if (m_bd.Line(p) > 1)
00517        return MatchAnyCenter(p);
00518     else if (m_bd.Pos(p) > 1)
00519         return MatchAnyEdge(p);
00520     else
00521         return false;
00522 }
00523 
00524 /** Procedural matching function - used to initialize the table. */
00525 template<class BOARD>
00526 bool GoUctPatterns<BOARD>::MatchAnyPattern(const GoBoard& bd, SgPoint p)
00527 {
00528     SG_ASSERT(bd.IsEmpty(p));
00529     const int nuBlack = bd.NumNeighbors(p, SG_BLACK);
00530     const int nuWhite = bd.NumNeighbors(p, SG_WHITE);
00531 
00532     // Quick refutation using the incremental neighbor counts of the board
00533     // All patterns have at least one adjacent stone
00534     if (nuBlack == 0 && nuWhite == 0)
00535         return false;
00536 
00537     // Filter edge moves on (1,1) points in corners
00538     if (bd.Pos(p) == 1)
00539         return false;
00540     if (bd.Line(p) == 1)
00541         return MatchEdge(bd, p, nuBlack, nuWhite);
00542     else // Center
00543         return   MatchHane(bd, p, nuBlack, nuWhite)
00544               || MatchCut(bd, p);
00545 }
00546 
00547 template<class BOARD>
00548 inline int GoUctPatterns<BOARD>::OtherDir(int dir)
00549 {
00550     if (dir == SG_NS || dir == -SG_NS)
00551         return SG_WE;
00552     return SG_NS;
00553 }
00554 
00555 template<class BOARD>
00556 int GoUctPatterns<BOARD>::SetupCodedEdgePosition(GoBoard& bd, int code)
00557 {
00558     const SgPoint p = SgPointUtil::Pt(1, 3);
00559     int count = 0;
00560     for (int i = 4; i >= 0; --i) // decoding gives points in reverse order
00561     {
00562         const SgPoint nb = p + EdgeDirection(bd, p, i);
00563         int c = code % 3;
00564         code /= 3;
00565         if (c != SG_EMPTY)
00566         {
00567             ++count;
00568             bd.Play(nb, c);
00569         }
00570     }
00571     return count;
00572 }
00573 
00574 template<class BOARD>
00575 int GoUctPatterns<BOARD>::SetupCodedPosition(GoBoard& bd, int code)
00576 {
00577     const SgPoint p = SgPointUtil::Pt(3, 3);
00578     int count = 0;
00579     for (int i = 7; i >= 0; --i) // decoding gives points in reverse order
00580     {
00581         const SgPoint nb = p + SgNb8Iterator::Direction(i);
00582         int c = code % 3;
00583         code /= 3;
00584         if (c != SG_EMPTY)
00585         {
00586             ++count;
00587             bd.Play(nb, c);
00588         }
00589     }
00590     return count;
00591 }
00592 
00593 //----------------------------------------------------------------------------
00594 
00595 #endif // GOUCT_PATTERNS_H


17 Jun 2010 Doxygen 1.4.7