00001 //---------------------------------------------------------------------------- 00002 /** @file StoneBoard.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "BoardUtils.hpp" 00007 #include "StoneBoard.hpp" 00008 00009 using namespace benzene; 00010 00011 //---------------------------------------------------------------------------- 00012 00013 StoneBoard::StoneBoard() 00014 : m_const(0), 00015 m_hash(0, 0) 00016 { 00017 } 00018 00019 StoneBoard::StoneBoard(unsigned size) 00020 : m_const(&ConstBoard::Get(size)), 00021 m_hash(size, size) 00022 { 00023 StartNewGame(); 00024 } 00025 00026 StoneBoard::StoneBoard(unsigned width, unsigned height) 00027 : m_const(&ConstBoard::Get(width, height)), 00028 m_hash(width, height) 00029 { 00030 StartNewGame(); 00031 } 00032 00033 StoneBoard::StoneBoard(unsigned width, unsigned height, const std::string& str) 00034 : m_const(&ConstBoard::Get(width, height)), 00035 m_hash(width, height) 00036 { 00037 SetPosition(str); 00038 } 00039 00040 StoneBoard::~StoneBoard() 00041 { 00042 } 00043 00044 //---------------------------------------------------------------------------- 00045 00046 HexColor StoneBoard::GetColor(HexPoint cell) const 00047 { 00048 HexAssert(Const().IsValid(cell)); 00049 if (IsBlack(cell)) return BLACK; 00050 if (IsWhite(cell)) return WHITE; 00051 return EMPTY; 00052 } 00053 00054 bitset_t StoneBoard::GetLegal() const 00055 { 00056 bitset_t legal; 00057 if (IsPlayed(RESIGN)) 00058 return legal; 00059 00060 legal = ~GetPlayed() & Const().GetCells(); 00061 legal.set(RESIGN); 00062 00063 // Swap is available only when 4 edges and exactly 00064 // one cell have been played 00065 if (GetPlayed().count() == 5) 00066 { 00067 HexAssert(!IsPlayed(SWAP_PIECES)); 00068 HexAssert(GetColor(FIRST_TO_PLAY).count() >= 3); 00069 HexAssert((GetPlayed(FIRST_TO_PLAY)).count() == 3); 00070 HexAssert(GetColor(!FIRST_TO_PLAY).count() == 2); 00071 legal.set(SWAP_PIECES); 00072 } 00073 HexAssert(Const().IsValid(legal)); 00074 return legal; 00075 } 00076 00077 bool StoneBoard::IsLegal(HexPoint cell) const 00078 { 00079 HexAssert(Const().IsValid(cell)); 00080 return GetLegal().test(cell); 00081 } 00082 00083 BoardIterator StoneBoard::Stones(HexColorSet colorset) const 00084 { 00085 if (!m_stones_calculated) 00086 { 00087 for (int i = 0; i < NUM_COLOR_SETS; ++i) 00088 { 00089 m_stones_list[i].clear(); 00090 for (BoardIterator p(Const().EdgesAndInterior()); p; ++p) 00091 if (HexColorSetUtil::InSet(GetColor(*p), (HexColorSet)i)) 00092 m_stones_list[i].push_back(*p); 00093 m_stones_list[i].push_back(INVALID_POINT); 00094 } 00095 m_stones_calculated = true; 00096 } 00097 return BoardIterator(m_stones_list[colorset]); 00098 } 00099 00100 //---------------------------------------------------------------------------- 00101 00102 void StoneBoard::MarkAsDirty() 00103 { 00104 m_stones_calculated = false; 00105 } 00106 00107 void StoneBoard::AddColor(HexColor color, const bitset_t& b) 00108 { 00109 HexAssert(HexColorUtil::isBlackWhite(color)); 00110 m_stones[color] |= b; 00111 HexAssert(IsBlackWhiteDisjoint()); 00112 if (b.any()) MarkAsDirty(); 00113 } 00114 00115 void StoneBoard::RemoveColor(HexColor color, const bitset_t& b) 00116 { 00117 HexAssert(HexColorUtil::isBlackWhite(color)); 00118 m_stones[color] = m_stones[color] - b; 00119 HexAssert(IsBlackWhiteDisjoint()); 00120 if (b.any()) MarkAsDirty(); 00121 } 00122 00123 void StoneBoard::SetColor(HexColor color, HexPoint cell) 00124 { 00125 HexAssert(HexColorUtil::isValidColor(color)); 00126 HexAssert(Const().IsValid(cell)); 00127 00128 if (color == EMPTY) { 00129 for (BWIterator it; it; ++it) 00130 m_stones[*it].reset(cell); 00131 } else { 00132 m_stones[color].set(cell); 00133 HexAssert(IsBlackWhiteDisjoint()); 00134 } 00135 00136 MarkAsDirty(); 00137 } 00138 00139 void StoneBoard::SetColor(HexColor color, const bitset_t& bs) 00140 { 00141 /** @todo Should we make this support EMPTY color too? */ 00142 HexAssert(HexColorUtil::isBlackWhite(color)); 00143 HexAssert(Const().IsValid(bs)); 00144 00145 m_stones[color] = bs; 00146 HexAssert(IsBlackWhiteDisjoint()); 00147 00148 MarkAsDirty(); 00149 } 00150 00151 void StoneBoard::SetPlayed(const bitset_t& played) 00152 { 00153 m_played = played; 00154 ComputeHash(); 00155 MarkAsDirty(); 00156 } 00157 00158 //---------------------------------------------------------------------------- 00159 00160 void StoneBoard::ComputeHash() 00161 { 00162 // do not include swap in hash value 00163 bitset_t mask = m_played & Const().GetLocations(); 00164 m_hash.Compute(m_stones[BLACK] & mask, m_stones[WHITE] & mask); 00165 } 00166 00167 void StoneBoard::StartNewGame() 00168 { 00169 m_played.reset(); 00170 for (BWIterator it; it; ++it) 00171 { 00172 m_stones[*it].reset(); 00173 PlayMove(*it, HexPointUtil::colorEdge1(*it)); 00174 PlayMove(*it, HexPointUtil::colorEdge2(*it)); 00175 } 00176 ComputeHash(); 00177 MarkAsDirty(); 00178 } 00179 00180 void StoneBoard::PlayMove(HexColor color, HexPoint cell) 00181 { 00182 HexAssert(HexColorUtil::isBlackWhite(color)); 00183 HexAssert(Const().IsValid(cell)); 00184 00185 m_played.set(cell); 00186 if (Const().IsLocation(cell)) 00187 m_hash.Update(color, cell); 00188 SetColor(color, cell); 00189 00190 MarkAsDirty(); 00191 } 00192 00193 void StoneBoard::UndoMove(HexPoint cell) 00194 { 00195 HexAssert(Const().IsValid(cell)); 00196 HexColor color = GetColor(cell); 00197 HexAssert(color != EMPTY); 00198 00199 m_played.reset(cell); 00200 if (Const().IsLocation(cell)) 00201 m_hash.Update(color, cell); 00202 SetColor(EMPTY, cell); 00203 00204 MarkAsDirty(); 00205 } 00206 00207 //---------------------------------------------------------------------------- 00208 00209 void StoneBoard::RotateBoard() 00210 { 00211 m_played = BoardUtils::Rotate(Const(), m_played); 00212 for (BWIterator it; it; ++it) 00213 m_stones[*it] = BoardUtils::Rotate(Const(), m_stones[*it]); 00214 ComputeHash(); 00215 MarkAsDirty(); 00216 } 00217 00218 bool StoneBoard::IsSelfRotation() const 00219 { 00220 if (m_stones[BLACK] != BoardUtils::Rotate(Const(), m_stones[BLACK])) 00221 return false; 00222 if (m_stones[WHITE] != BoardUtils::Rotate(Const(), m_stones[WHITE])) 00223 return false; 00224 return true; 00225 } 00226 00227 void StoneBoard::MirrorBoard() 00228 { 00229 m_played = BoardUtils::Mirror(Const(), m_played); 00230 for (BWIterator it; it; ++it) 00231 m_stones[*it] = BoardUtils::Mirror(Const(), m_stones[*it]); 00232 ComputeHash(); 00233 MarkAsDirty(); 00234 } 00235 00236 //---------------------------------------------------------------------------- 00237 00238 BoardID StoneBoard::GetBoardID() const 00239 { 00240 /** Packs each interior cell into 2 bits. 00241 @note Assumes all valid HexColors lie between [0,2]. */ 00242 std::size_t n = (Width() * Height() + 3) / 4 * 4; 00243 00244 /** @note When this code was written, the cells were iterated over 00245 in the order (a1, b1, c1, ..., a2, b2, c2, ... , etc). Any 00246 changes to the order in Interior() will break all existing 00247 databases that use BoardID as a lookup, unless this method is 00248 updated to always compute in the above order. */ 00249 std::size_t i = 0; 00250 std::vector<byte> val(n, 0); 00251 bitset_t played = GetPlayed(); 00252 for (BoardIterator p(Const().Interior()); p; ++p, ++i) 00253 val[i] = (played.test(*p)) 00254 ? static_cast<byte>(GetColor(*p)) 00255 : static_cast<byte>(EMPTY); 00256 00257 BoardID id; 00258 for (i = 0; i < n; i += 4) 00259 id.push_back(static_cast<byte>(val[i] 00260 + (val[i+1] << 2) 00261 + (val[i+2] << 4) 00262 + (val[i+3] << 6))); 00263 return id; 00264 } 00265 00266 std::string StoneBoard::GetBoardIDString() const 00267 { 00268 std::string hexval[16] = {"0", "1", "2", "3", "4", "5", "6", "7", 00269 "8", "9", "a", "b", "c", "d", "e", "f"}; 00270 BoardID brdID = GetBoardID(); 00271 std::string idString; 00272 for (std::size_t i = 0; i < brdID.size(); ++i) { 00273 byte b = brdID[i]; 00274 idString += hexval[((b >> 4) & 0xF)]; 00275 idString += hexval[b & 0x0F]; 00276 } 00277 return idString; 00278 } 00279 00280 void StoneBoard::SetPosition(const StoneBoard& brd) 00281 { 00282 StartNewGame(); 00283 SetColor(BLACK, brd.GetBlack()); 00284 SetColor(WHITE, brd.GetWhite()); 00285 SetPlayed(brd.GetPlayed()); 00286 } 00287 00288 void StoneBoard::SetPositionOnlyPlayed(const StoneBoard& brd) 00289 { 00290 StartNewGame(); 00291 SetColor(BLACK, brd.GetBlack() & brd.GetPlayed()); 00292 SetColor(WHITE, brd.GetWhite() & brd.GetPlayed()); 00293 SetPlayed(brd.GetPlayed()); 00294 } 00295 00296 void StoneBoard::SetPosition(const BoardID& id) 00297 { 00298 std::size_t n = (Width() * Height() + 3) / 4 * 4; 00299 HexAssert(id.size() == n / 4); 00300 00301 std::vector<byte> val(n, 0); 00302 for (std::size_t i = 0; i < n; i += 4) 00303 { 00304 byte packed = id[i/4]; 00305 val[i] = packed & 0x3; 00306 val[i+1] = (packed >> 2) & 0x3; 00307 val[i+2] = (packed >> 4) & 0x3; 00308 val[i+3] = (packed >> 6) & 0x3; 00309 } 00310 00311 /** @note This depends on the order defined by Interior(). 00312 See note in implementation of GetBoardID(). */ 00313 StartNewGame(); 00314 std::size_t i = 0; 00315 for (BoardIterator p(Const().Interior()); p; ++p, ++i) 00316 { 00317 HexColor color = static_cast<HexColor>(val[i]); 00318 if (color == BLACK || color == WHITE) 00319 PlayMove(color, *p); 00320 } 00321 00322 ComputeHash(); 00323 MarkAsDirty(); 00324 } 00325 00326 void StoneBoard::SetPosition(const std::string& str) 00327 { 00328 /** @note This depends on the order defined by Interior(). */ 00329 StartNewGame(); 00330 int cell = 0; 00331 for (std::size_t i = 0; 00332 i < str.size() && cell < Width() * Height(); 00333 ++i) 00334 { 00335 int x = cell % Width(); 00336 int y = cell / Width(); 00337 HexPoint p = HexPointUtil::coordsToPoint(x, y); 00338 if (str[i] == '.') 00339 cell++; 00340 else if (str[i] == 'B') 00341 { 00342 PlayMove(BLACK, p); 00343 cell++; 00344 } 00345 else if (str[i] == 'W') 00346 { 00347 PlayMove(WHITE, p); 00348 cell++; 00349 } 00350 else if (str[i] == 'b') 00351 { 00352 SetColor(BLACK, p); 00353 cell++; 00354 } 00355 else if (str[i] == 'w') 00356 { 00357 SetColor(WHITE, p); 00358 cell++; 00359 } 00360 } 00361 ComputeHash(); 00362 MarkAsDirty(); 00363 } 00364 00365 //---------------------------------------------------------------------------- 00366 00367 std::string StoneBoard::Write() const 00368 { 00369 return Write(EMPTY_BITSET); 00370 } 00371 00372 std::string StoneBoard::Write(const bitset_t& b) const 00373 { 00374 std::ostringstream out; 00375 out << '\n'; 00376 out << " " << HashUtil::toString(Hash()) << '\n'; 00377 out << " "; 00378 for (int i = 0; i < Width(); i++) 00379 out << (char)('a' + i) << " "; 00380 out << '\n'; 00381 for (int i = 0; i < Height(); i++) 00382 { 00383 for (int j = 0; j < i; j++) 00384 out << " "; 00385 if (i + 1 < 10) 00386 out << " "; 00387 out << (i + 1) << "\\"; 00388 for (int j = 0; j < Width(); j++) 00389 { 00390 HexPoint p = HexPointUtil::coordsToPoint(j, i); 00391 if (j) 00392 out << " "; 00393 if (b.test(p)) 00394 out << "*"; 00395 else if (IsBlack(p) && IsPlayed(p)) 00396 out << "B"; 00397 else if (IsBlack(p)) 00398 out << "b"; 00399 else if (IsWhite(p) && IsPlayed(p)) 00400 out << "W"; 00401 else if (IsWhite(p)) 00402 out << "w"; 00403 else 00404 out << "."; 00405 } 00406 out << "\\"; 00407 out << (i + 1); 00408 out << '\n'; 00409 } 00410 for (int j = 0; j < Height(); j++) 00411 out << " "; 00412 out << " "; 00413 for (int i = 0; i < Width(); i++) 00414 out << (char)('a' + i) << " "; 00415 return out.str(); 00416 } 00417 00418 bool StoneBoard::IsBlackWhiteDisjoint() 00419 { 00420 if ((m_stones[BLACK] & m_stones[WHITE]).any()) 00421 { 00422 for (BWIterator it; it; ++it) 00423 LogWarning() << HexPointUtil::ToString(m_stones[*it]) << '\n'; 00424 return false; 00425 } 00426 return true; 00427 } 00428 00429 //----------------------------------------------------------------------------