00001 //---------------------------------------------------------------------------- 00002 /** @file SwapCheck.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "BoardUtils.hpp" 00007 #include "SwapCheck.hpp" 00008 00009 using namespace benzene; 00010 00011 //---------------------------------------------------------------------------- 00012 00013 namespace { 00014 00015 //---------------------------------------------------------------------------- 00016 00017 bool s_swapLoaded = false; 00018 00019 /** Contains moves to swap for each boardsize. 00020 Use strings of the form "nxn" to index the map for an (n, n) 00021 board. */ 00022 std::map<std::string, std::set<HexPoint> > s_swapMoves; 00023 00024 /** Loads swap moves for each boardsize from the given file. 00025 Ignores lines begining with '#'. On lines not begining with '#', 00026 expects a string of the form "nxn" and the name of a HexPoint: 00027 this pair denotes a move to swap on an nxn board. The remainder of 00028 the line is not looked at. */ 00029 void LoadSwapMoves(const std::string& name) 00030 { 00031 using namespace boost::filesystem; 00032 path swap_path = path(ABS_TOP_SRCDIR) / "share"; 00033 path swap_list = swap_path / name; 00034 swap_list.normalize(); 00035 std::string swap_file = swap_list.native_file_string(); 00036 LogInfo() << "SwapCheck: Loading swap moves: '" << swap_file << "'...\n"; 00037 s_swapMoves.clear(); 00038 std::ifstream s(swap_file.c_str()); 00039 if (!s) 00040 throw BenzeneException("SwapCheck: could not open list!\n"); 00041 std::string line; 00042 std::size_t lineNumber = 0; 00043 while (std::getline(s, line)) 00044 { 00045 lineNumber++; 00046 if (line[0] == '#') 00047 continue; 00048 if (line.size() < 6) // skip (nearly) empty lines 00049 continue; 00050 std::string boardSizeStr; 00051 std::string pointStr; 00052 std::istringstream ss(line); 00053 ss >> boardSizeStr; 00054 ss >> pointStr; 00055 HexPoint point = HexPointUtil::FromString(pointStr); 00056 if (point == INVALID_POINT) 00057 LogWarning() << "SwapCheck: line " << lineNumber 00058 << ": invalid cell!\n"; 00059 else 00060 s_swapMoves[boardSizeStr].insert(point); 00061 } 00062 s.close(); 00063 s_swapLoaded = true; 00064 } 00065 00066 //---------------------------------------------------------------------------- 00067 00068 } // anonymous namespace 00069 00070 //---------------------------------------------------------------------------- 00071 00072 bool SwapCheck::PlaySwap(const Game& gameState, HexColor toPlay) 00073 { 00074 if (gameState.AllowSwap() 00075 && (1 == gameState.History().size()) 00076 && (!FIRST_TO_PLAY == toPlay)) 00077 { 00078 const StoneBoard& brd = gameState.Board(); 00079 HexAssert(1 == brd.NumStones()); 00080 00081 // If board has unequal dimensions, we want to traverse the 00082 // shorter distance. 00083 if (brd.Width() != brd.Height()) 00084 { 00085 if ((brd.Width() > brd.Height() && toPlay == !VERTICAL_COLOR) || 00086 (brd.Width() < brd.Height() && toPlay == VERTICAL_COLOR)) 00087 { 00088 LogInfo() << "SwapCheck: swapping to get shorter side.\n"; 00089 return true; 00090 } 00091 } 00092 else 00093 { 00094 if (!s_swapLoaded) 00095 LoadSwapMoves("swap-moves.txt"); 00096 HexPoint firstMove = gameState.History().back().point(); 00097 if (toPlay == VERTICAL_COLOR) 00098 // Swap decisions assume VERTICAL_COLOR was FIRST_TO_PLAY, 00099 // so we mirror the first move if this is not the case 00100 // (i.e. to consider an equivalent decision). 00101 firstMove = BoardUtils::Mirror(brd.Const(), firstMove); 00102 std::ostringstream os; 00103 os << brd.Width() << 'x' << brd.Height(); 00104 if (s_swapMoves[os.str()].count(firstMove) > 0) 00105 { 00106 LogInfo() << "SwapCheck: playing swap.\n"; 00107 return true; 00108 } 00109 } 00110 LogInfo() << "SwapCheck: opting not to swap.\n"; 00111 } 00112 return false; 00113 } 00114 00115 //----------------------------------------------------------------------------