//检查该点是否无气,并处理 void Go::isDie(int x, int y) { //是否在棋盘上(防止path数组越界。。。) if (path[x][y] || ans == false) { //已经走过(path数组标记),或已经发现气,取消检测 return; } //为空应停止本次检查 if (go[x][y].isEmpty()) { ans = false; } else { Cell ce = go[x][y]; path[x][y] = true;//标记走过,带颜色的点 cpath.push_back(ce);//记录每个标记(空不可标记,会影响后续检查),检查完后清理标记 if (go[x][y].isSameColor(checkcolor)) {//相同颜色,继续拓展 die.push_back(ce);//这样应该是复制一个对象,非引用(后面的函数调用使用修改ce不影响die存放的信息) = = int check[4][2]; prepareExt(x, y, check); for (int i = 0; i < 4; i++) { if (isOnBoard(check[i][0], check[i][1])) { isDie(check[i][0], check[i][1]); } } } } }
/* Clears a group of stones from the board param player: color of the group param x, y: coordinates of a stone within the group */ void GoBoard::clearGroup( Player player, int x, int y ) { //clear the current node grid[x][y]->setPlayer( EMPTY ); //array's for the orthogonal neighbors int xnew[4] = { x-1, x, x+1, x }; int ynew[4] = { y, y-1, y, y+1 }; //iterate through each neighbor for( int i = 0; i < 4; i++ ) { //make sure we don't go outside the board if( isOnBoard( xnew[i],ynew[i] )) { //make sure the stone is of the correct player if( grid[xnew[i]][ynew[i]]->isPlayer( player )) { //clear the stone and any stones of the same player attached to it clearGroup( player, xnew[i], ynew[i] ); } } } }
void Go::isBlackEmpty(int x, int y) { if (flagEmpty[x][y]) { return; } if (go[x][y].isEmpty()) { flagEmpty[x][y] = true; tcountEmpty++; int check[4][2]; prepareExt(x, y, check); for (int i = 0; i < 4; i++) { if (isOnBoard(check[i][0], check[i][1])) { isBlackEmpty(check[i][0], check[i][1]); } } } else { if (go[x][y].isSameColor(s_black)) { flagBlack = true; } else { flagWrite = true; } } }
/* Determines if the position is available to have a stone placed in it param x,y: integer coordinates of the stone to place return bool: whether a stone can be placed there */ bool GoBoard::isValidMove( int x, int y) { if( isOnBoard( x, y ) && isPlayer( EMPTY, x, y ) && !isKo( x, y )) { return true; } return false; }
void GraphicalBoardFrame::prepare() { drawBoard(m_board); drawMove(m_candidate); if (isOnBoard(m_arrowRoot)) drawArrow(m_arrowRoot, m_arrowDirection); generateBoardPixmap(&m_pixmap); update(); }
/* core function: recursively traces through every stone that is orthogonally connected starting at stone x,y. as it traverses it counts and updates the attributes Size, Liberties, and Owner, and returns them wrapped in a Group structure param x,y: coordinates of a stone within a group param currGroup: a copy of a Group object that holds all attributes returns currGroup after traversing all stones in the group */ Group GoBoard::getGroup( int x, int y, Group currGroup ) { //coordinates of all neighbor stones int xnew[] = { x-1, x, x+1, x }; int ynew[] = { y, y-1, y, y+1 }; //set as counted to prevent double count grid[x][y]->setCounted(); //iterate through every neighbor for( int i = 0; i < 4; i++ ) { //verify we're on the board if( isOnBoard( xnew[i], ynew[i] )) { //verify we have the same player if( grid[xnew[i]][ynew[i]]->isPlayer(currGroup.player)) { //check if already counted if( !grid[xnew[i]][ynew[i]]->isCounted()) { currGroup = getGroup( xnew[i], ynew[i], currGroup ); } } //if it is not the same player then we have to determine if ownership //of the group has changed for empty territories else if( currGroup.owner == EMPTY || grid[xnew[i]][ynew[i]]->isPlayer(currGroup.owner)) { //if we have run into the same owner, or the owner hasn't been set //then the piece we've run into is the owner currGroup.owner = (Player)grid[xnew[i]][ynew[i]]->getPlayer(); } else { //if we've run into something different than the owner //then the territory is contested, once it's marked as DOMI //it can't be scored for either player currGroup.owner = (Player)DOMI; } } } //increment the group size and find any liberties next to the current stone currGroup.size += 1; currGroup.liberties += countNeighbors( EMPTY, x, y ); //debug for the attributes of each stone as it's traversed //std::cout<<"x: "<< x <<" y: "<< y << " lib: "<<countNeighbors( EMPTY, x, y )<<std::endl; return currGroup; }
/* Counts the all neighbors of a specific player around the provided stone. param target: the player type we are counting param x and y: the grid coordinates of the center stone return count: the number of neighbors of target color */ int GoBoard::countNeighbors( Player target, int x, int y ) { int count = 0; int xnew[4] = { x-1, x, x+1, x }; int ynew[4] = { y, y-1, y, y+1 }; for( int i = 0; i<4; i++ ) { if( isOnBoard( xnew[i], ynew[i] )) { if( grid[xnew[i]][ynew[i]]->isPlayer(target)) { count += 1; } } } return count; }
//下棋后检查吃子 void Go::checkStarus(int x, int y) { int check[4][2]; prepareExt(x, y, check); //此处4个检查应该互不影响 for (int i = 0; i < 4; i++) { ans = true;//发现empty标记false; die.clear();//先删除上次状态 //不在棋盘时,检查无意义 if (isOnBoard(check[i][0], check[i][1])) { isDie(check[i][0], check[i][1]); chearPath(); //检查玩之后,清理path待下次检查,返回die,ans标记是否有效 if (ans) { for (Cell ce : die) { tp.push_back(ce); } } } } }
int processPlace(PlayerState* ps, Command* cmd) { if ( (ps == NULL) || (cmd == NULL) ) { return (-1); } //check and convert the row and column fields if ( !isGoodRow(cmd->row) || !isGoodCol(cmd->col) ) { return (-1); } //yes more non-switch statements if (cmd->shipNum == SHIP2) { //if its outside of the board area then error if (!isOnBoard(cmd->row, cmd->col, cmd->dir, 2) || (ps->ship2Life > 0)) { return (-1); } //if the slots are already taken -- error if (cmd->dir == DIR_R) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] = cmd->shipNum; //We can totally go overboard and assign this to be cmd->shipNum as well ps->ship2Life = 2; } else if (cmd->dir == DIR_D) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->ship2Life = 2; } else { //should never be here in this else return (-1); } } else if (cmd->shipNum == SHIP3) { if (!isOnBoard(cmd->row, cmd->col, cmd->dir, 3) || (ps->ship3Life > 0)) { return (-1); } //if the slots are already taken -- error if (cmd->dir == DIR_R) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] = cmd->shipNum; ps->ship3Life = 3; } else if (cmd->dir == DIR_D) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->ship3Life = 3; } else { //should never be here in this else return (-1); } } else if (cmd->shipNum == SHIP4) { if (!isOnBoard(cmd->row, cmd->col, cmd->dir, 4) || (ps->ship4Life > 0)) { return (-1); } //if the slots are already taken -- error if (cmd->dir == DIR_R) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 3] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 3] = cmd->shipNum; ps->ship4Life = 4; } else if (cmd->dir == DIR_D) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 3][COL_TO_NUM(cmd->col)] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 3][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->ship4Life = 4; } else { //should never be here in this else return (-1); } } else if (cmd->shipNum == SHIP5) { if (!isOnBoard(cmd->row, cmd->col, cmd->dir, 5) || (ps->ship5Life > 0)) { return (-1); } //if the slots are already taken -- error if (cmd->dir == DIR_R) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 3] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 4] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 1] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 2] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 3] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col) + 4] = cmd->shipNum; ps->ship5Life = 5; } else if (cmd->dir == DIR_D) { if ( (ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 3][COL_TO_NUM(cmd->col)] != EMPTY_CELL) || ( ps->board[ROW_TO_NUM(cmd->row) + 4][COL_TO_NUM(cmd->col)] != EMPTY_CELL) ) { return (-1); } ps->board[ROW_TO_NUM(cmd->row)][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 1][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 2][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 3][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->board[ROW_TO_NUM(cmd->row) + 4][COL_TO_NUM(cmd->col)] = cmd->shipNum; ps->ship5Life = 5; } else { //should never be here in this else return (-1); } } else { return (-1); } return (0); }
/* Probes adjecent stones and determines if any neighbor groups no longer have liberties param enemy: the color of the opponents stones param x, y: coordinates of the newly placed stone return Bool, KOflag, returns true if a state of KO may be possible this flag, combined with the flag in the main step function, determines if there is in fact a KO state. */ bool GoBoard::effectEnemies( Player enemy, int x, int y ) { //flags to see if a state of KO may be possible int killSize = 0; //size of last gorup killed, used to test for KO, and scoring int numKilled = 0; //groups killed, used to test for KO //create a group to be reused to test all enemies Group enemyGroup; enemyGroup.player = enemy; enemyGroup.owner = (Player)EMPTY; enemyGroup.size = 0; enemyGroup.liberties = 0; //array's to represent orthogonal neighbors int xnew[4] = { x-1, x, x+1, x }; int ynew[4] = { y, y-1, y, y+1 }; //iterate through each neighbor for( int i = 0; i < 4; i++ ) { //make sure we don't go outside the board if( isOnBoard( xnew[i], ynew[i] )) { //see if the stone is an enemy and has NOT been counted yet if( grid[xnew[i]][ynew[i]]->isPlayer( enemy ) && !grid[xnew[i]][ynew[i]]->isCounted() ) { enemyGroup.size = 0; enemyGroup.liberties = 0; enemyGroup = getGroup( xnew[i], ynew[i], enemyGroup ); if( enemyGroup.liberties == 0 ) { //set the killSize when a group is killed killSize = enemyGroup.size; numKilled += 1; //sets the KO position temporarily //will be cleared in the step function if //a ko is not present koPosX = xnew[i]; koPosY = ynew[i]; switch( enemyGroup.player ) { case(WHITE): { blackPrisoners += killSize; break; } case(BLACK): { whitePrisoners += killSize; break; } default: { break; } } clearGroup( enemy, xnew[i], ynew[i] ); } } } } //if a single group of size 1 is killed then we need to return that a //KO may be possible depending on the size of the player group if( numKilled == 1 && killSize == 1) { return true; } else { return false; } }
void GraphicalBoardFrame::tileClicked(const QSize &tileLocation, const QMouseEvent * /* event */) { Quackle::Board::TileInformation info(m_board.tileInformation(tileLocation.height(), tileLocation.width())); if (info.tileType == Quackle::Board::LetterTile) return; if (m_arrowRoot == tileLocation) { ++m_arrowDirection; if (m_arrowDirection == ArrowWorm) m_arrowDirection = s_firstArrowDirection; } else m_arrowDirection = s_firstArrowDirection; m_arrowRoot = tileLocation; Quackle::Move originalMove; Quackle::LetterString hoppedTiles; QSize currentTile(tileLocation); while (true) { const QSize previousTile = currentTile - arrowVector(); bool stopHere; if (!isOnBoard(previousTile)) stopHere = true; else { Quackle::Board::TileInformation previousTileInformation(m_board.tileInformation(previousTile.height(), previousTile.width())); stopHere = previousTileInformation.tileType != Quackle::Board::LetterTile; } if (stopHere) { const bool horizontal = m_arrowDirection == ArrowRight; originalMove = Quackle::Move::createPlaceMove(currentTile.height(), currentTile.width(), horizontal, hoppedTiles); break; } hoppedTiles += QUACKLE_PLAYED_THRU_MARK; currentTile = previousTile; } prettifyAndSetLocalCandidate(originalMove); // TODO work some cleverness so we can do this! //emit setCandidateMove(Quackle::Move::createNonmove()); for (QSize currentTile(0, 0); currentTile.height() < m_boardSize.height(); currentTile.setHeight(currentTile.height() + 1)) for (currentTile.setWidth(0); currentTile.width() < m_boardSize.width(); currentTile.setWidth(currentTile.width() + 1)) if (currentTile != tileLocation) { TileWidget *tile = tileAt(currentTile); if (!tile) continue; tile->setArrowDirection(NoArrow); } prepare(); }
void GraphicalBoardFrame::appendHandler(const QString &text, bool shiftPressed) { if (!isOnBoard(m_arrowRoot)) return; if (!hasCandidate()) return; Quackle::LetterString appendedLetterString(QuackleIO::Util::encode(text)); if (appendedLetterString.length() == 0 || Quackle::String::front(appendedLetterString) < QUACKLE_FIRST_LETTER) return; if (shiftPressed) appendedLetterString = Quackle::String::setBlankness(appendedLetterString); else appendedLetterString = Quackle::String::clearBlankness(appendedLetterString); Quackle::Move newCandidate(m_candidate); Quackle::LetterString newTiles(m_candidate.tiles() + appendedLetterString); if (!m_ignoreRack && !m_rack.contains(Quackle::String::usedTiles(newTiles))) { Quackle::LetterString blankedNewTiles(m_candidate.tiles() + Quackle::String::setBlankness(appendedLetterString)); if (m_rack.contains(Quackle::String::usedTiles(blankedNewTiles))) { newTiles = blankedNewTiles; } } newCandidate.setTiles(newTiles); Quackle::LetterString hoppedTiles; QSize currentTile(m_arrowRoot); while (true) { const QSize nextTile = currentTile + arrowVector(); bool stopHere = false; bool resetArrowAfter = false; if (!isOnBoard(nextTile)) { stopHere = true; } else { Quackle::Board::TileInformation nextTileInformation(m_board.tileInformation(nextTile.height(), nextTile.width())); if (nextTileInformation.tileType != Quackle::Board::LetterTile) stopHere = true; } if (stopHere) { newCandidate.setTiles(newCandidate.tiles() + hoppedTiles); if (resetArrowAfter) resetArrow(); else m_arrowRoot = nextTile; break; } hoppedTiles += QUACKLE_PLAYED_THRU_MARK; currentTile = nextTile; } prettifyAndSetLocalCandidate(newCandidate); }
void GraphicalBoardFrame::backspaceHandler() { unsigned int hoppedTiles = 0; QSize currentTile(m_arrowRoot); while (true) { const QSize previousTile = currentTile - arrowVector(); bool stopHere; if (!isOnBoard(previousTile)) stopHere = true; else { Quackle::Board::TileInformation previousTileInformation(m_board.tileInformation(previousTile.height(), previousTile.width())); stopHere = previousTileInformation.tileType != Quackle::Board::LetterTile; } ++hoppedTiles; if (stopHere) { m_arrowRoot = previousTile; break; } currentTile = previousTile; } Quackle::LetterString tiles(m_candidate.tiles()); if (hoppedTiles > tiles.length()) tiles = Quackle::LetterString(); else tiles = Quackle::String::left(m_candidate.tiles(), m_candidate.tiles().length() - hoppedTiles); if (tiles.empty()) { Quackle::Move originalMove; Quackle::LetterString hoppedLetters; currentTile = m_arrowRoot; while (true) { const QSize previousTile = currentTile - arrowVector(); bool stopHere; if (!isOnBoard(previousTile)) stopHere = true; else { Quackle::Board::TileInformation previousTileInformation(m_board.tileInformation(previousTile.height(), previousTile.width())); stopHere = previousTileInformation.tileType != Quackle::Board::LetterTile; } if (stopHere) { const bool horizontal = m_arrowDirection == ArrowRight; m_candidate = Quackle::Move::createPlaceMove(currentTile.height(), currentTile.width(), horizontal, hoppedLetters); break; } hoppedLetters += QUACKLE_PLAYED_THRU_MARK; currentTile = previousTile; } } else { m_candidate.setTiles(tiles); } ensureCandidatePlacedProperly(); prettifyAndSetLocalCandidate(m_candidate); }