Poprawione funkcje sprawdzające możliwości ruchów i bić

This commit is contained in:
Bartłomiej Pluta
2016-05-21 23:05:16 +02:00
parent 8f1dc27b81
commit ffde3025d2
5 changed files with 200 additions and 121 deletions

View File

@@ -69,6 +69,9 @@ public:
// Czy bicie z danej pozycji jest możliwe
bool isPossibleBeating(Vector position);
// Czy istnieje możliwe bicie dla danego koloru
bool arePossibleGlobalBeatings(Color color);
// Kontener możliwych bić
std::list<Movement> getPossibleBeatings(Vector position);
@@ -76,7 +79,10 @@ public:
std::list<Movement> getPossibleMovements(Vector position);
// Pobierz możliwe ruchy wszystkich pionków danego koloru
std::list<Movement> getPossibleMovements(Color color);
std::list<Movement> getPossibleGlobalMovements(Color color);
// Pobierz możliwe bicia wszystkich pionków danego koloru
std::list<Movement> getPossibleGlobalBeatings(Color color);
// Usuń pionek z określonej pozycji
Pawn deletePawn(Vector position);

View File

@@ -22,17 +22,14 @@ private:
// Funkcja zwraca kolor kolejnego gracza z drzewa minimaks
Color getColorFromDepth(int depth);
// Funkcja wyceniająca na zasadzie trzech obszarów
int f1(Board& board, const Color& color);
// Funkcja heurystyczna
int evaluate(Board& board, const Color& color);
public:
// Konstruktor przyjmuje parametry: maksymalna głębokość algorytmu MiniMaks oraz kolor pionków
// sztucznej inteligencji
MiniMax(int depth_max, Color ai_color) : DEPTH_MAX(depth_max), AI_COLOR(ai_color) {}
// Funkcja heurystyczna
int evaluate(Board& board, const Color& color) { return f1(board, color); }
// Algorytm MiniMax z cięciem alfa-beta (funkcja zwraca wartość najlepszego ruchu, natomiast
// poprzez referencję zwraca najlepszy ruch best_movement
int alphabeta(Board board, int depth, int alpha, int beta, Movement& best_movement);

View File

@@ -17,6 +17,14 @@ void Board::initBoard()
for(int i=0; i<TILES_COUNT; i=i+2) createPawn(Vector(i, TILES_COUNT-1), CL_WHITE);
for(int i=1; i<TILES_COUNT; i=i+2) createPawn(Vector(i, TILES_COUNT-2), CL_WHITE);
for(int i=0; i<TILES_COUNT; i=i+2) createPawn(Vector(i, TILES_COUNT-3), CL_WHITE);
createPawn(Vector(5, 4), CL_WHITE);
// createPawn(Vector(4, 4), CL_BLACK);
// createPawn(Vector(5, 3), CL_BLACK);
// createPawn(Vector(6, 4), CL_BLACK);
// createPawn(Vector(5, 5), CL_BLACK);
board[5][4]->upgrade();
}
Pawn* Board::createPawn(Vector position, Color color)
@@ -145,56 +153,15 @@ Pawn* Board::selectPawn(Vector position)
bool Board::isPossibleBeating(Vector position)
{
// Pobieramy wskaźnik do pionka znajdującego się na zadanej pozycji
Pawn* position_pawn = getPawn(position);
// Jeżeli go tam nie ma, zwracamy false
if(!position_pawn) return false;
// Zabezpieczenie przed przekroczeniem indeksów
if((getPawn(position)->getPosition().x > 1 &&
getPawn(position)->getPosition().y > 1) &&
(getPawn(position)->getPosition().x < TILES_COUNT-1 &&
getPawn(position)->getPosition().y < TILES_COUNT-1))
// Jeżeli na polu przesuniętym o wektor [1,1] jest pionek, i jest on koloru przeciwnego
// a za pole zanim jest puste, zwracamy true
if(getPawn(position+Vector(1, 1)) &&
(getPawn(position+Vector(1, 1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(2, 2)))) return true;
// ... analogicznie w pozostałych kierunkach
// Zabezpieczenie przed przekroczeniem indeksów
if((getPawn(position)->getPosition().x > 1 &&
getPawn(position)->getPosition().y > 1) &&
(getPawn(position)->getPosition().x < TILES_COUNT-1 &&
getPawn(position)->getPosition().y < TILES_COUNT-1))
if(getPawn(position+Vector(1, -1)) &&
(getPawn(position+Vector(1, -1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(2, -2)))) return true;
// Zabezpieczenie przed przekroczeniem indeksów
if(getPawn(position)->getPosition().x > 1 &&
getPawn(position)->getPosition().y > 1)
if(getPawn(position+Vector(-1, 1)) &&
(getPawn(position+Vector(-1, 1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(-2, 2)))) return true;
// Zabezpieczenie przed przekroczeniem indeksów
if(getPawn(position)->getPosition().x > 1 &&
getPawn(position)->getPosition().y > 1)
if(getPawn(position+Vector(-1, -1)) &&
(getPawn(position+Vector(-1, -1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(-2, -2)))) return true;
return false;
return getPossibleBeatings(position).size();
}
bool Board::arePossibleGlobalBeatings(Color color)
{
return getPossibleGlobalBeatings(color).size();
}
std::list<Movement> Board::getPossibleBeatings(Vector position)
{
// Tworzymy nowy kontener na pozycje
@@ -206,28 +173,69 @@ std::list<Movement> Board::getPossibleBeatings(Vector position)
// Jeżeli go tam nie ma, zwracamy pusty kontener
if(!position_pawn) return beatings;
// Jeżeli na polu przesuniętym o wektor [1,1] jest pionek, i jest on koloru przeciwnego
// a za pole zanim jest puste, dorzucamy tą pozycję do kontenera
if(position.x < TILES_COUNT-1 && position.y < TILES_COUNT-1)
if(getPawn(position+Vector(1, 1)) &&
(getPawn(position+Vector(1, 1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(2, 2)))) beatings.push_back(Movement(position, position+Vector(2, 2)));
//// Bicie normalnymi pionkami
// ... analogicznie w pozostałych kierunkach
if(position.x < TILES_COUNT-1 && position.y > 1)
if(getPawn(position+Vector(1, -1)) &&
(getPawn(position+Vector(1, -1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(2, -2)))) beatings.push_back(Movement(position, position+Vector(2, -2)));
// Bicie o wektor [-2, -2]
if((position.x >= 2 && position.y >= 2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(-1, -1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(-1, -1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(-2, -2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(-2, -2))); // Dorzuć ruch do listy
if(position.x > 1 && position.y < TILES_COUNT-1)
if(getPawn(position+Vector(-1, 1)) &&
(getPawn(position+Vector(-1, 1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(-2, 2)))) beatings.push_back(Movement(position, position+Vector(-2, 2)));
// Bicie o wektor [-2, 2]
if((position.x >= 2 && position.y < TILES_COUNT-2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(-1, 1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(-1, 1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(-2, 2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(-2, 2))); // Dorzuć ruch do listy
if(position.x > 1 && position.y > 1)
if(getPawn(position+Vector(-1, -1)) &&
(getPawn(position+Vector(-1, -1))->getColor() != position_pawn->getColor()) &&
(!getPawn(position+Vector(-2, -2)))) beatings.push_back(Movement(position, position+Vector(-2, -2)));
// Bicie o wektor [2, 2]
if((position.x < TILES_COUNT-2 && position.y < TILES_COUNT-2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(1, 1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(1, 1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(2, 2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(2, 2))); // Dorzuć ruch do listy
// Bicie o wektor [2, -2]
if((position.x < TILES_COUNT-2 && position.y >= 2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(1, -1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(1, -1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(2, -2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(2, -2))); // Dorzuć ruch do listy
//// Bicie damkami
// Jeżeli pionek jest damką posiada dodatkowe możliwości bicia
if(getPawn(position)->isQueen())
{
// Bicie o wektor [-2, 0]
if((position.x >= 2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(-1, 0))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(-1, 0))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(-2, 0)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(-2, 0))); // Dorzuć ruch do listy
// Bicie o wektor [0, 2]
if((position.y < TILES_COUNT-2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(0, 1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(0, 1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(0, 2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(0, 2))); // Dorzuć ruch do listy
// Bicie o wektor [2, 0]
if((position.x < TILES_COUNT-2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(1, 0))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(1, 0))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(2, 0)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(2, 0))); // Dorzuć ruch do listy
// Bicie o wektor [0, -2]
if((position.y < TILES_COUNT-2) && // Jeżeli bicie nie wyjdzie poza planszę
(getPawn(position+Vector(0, -1))) && // Jeżeli istnieje bity pionek
(getPawn(position+Vector(0, -1))->getColor() != position_pawn->getColor()) && // Jeżeli jest to pionek przeciwnego koloru
(!getPawn(position+Vector(0, -2)))) // Jeżeli pole docelowe jest puste
beatings.push_back(Movement(position, position+Vector(0, -2))); // Dorzuć ruch do listy
}
// Zwracamy kontener
return beatings;
@@ -244,45 +252,104 @@ std::list<Movement> Board::getPossibleMovements(Vector position)
// Jeżeli pionek nie istnieje, zwróć pustą listę
if(!getPawn(position)) return movements;
// Jeżeli to ruch czarnego pionka
if(getPawn(position)->getColor() == CL_BLACK)
// Jeżeli pionek jest damką
if(getPawn(position)->isQueen())
{
// Zabezpieczenie przed przekroczeniem indeksów
if(getPawn(position)->getPosition().x > 0 &&
getPawn(position)->getPosition().y > 0)
// Jeżeli miejsce 1 jest wolne, wrzuć do listy
if(!getPawn(position+Vector(1, 1))) movements.push_back(Movement(position, position+Vector(1, 1)));
// Zabezpieczenie przed przekroczeniem indeksów
if(getPawn(position)->getPosition().x > 0 &&
getPawn(position)->getPosition().y > 0)
// Jeżeli miejsce 2 jest wolne, wrzuć do listy
if(!getPawn(position+Vector(-1, 1))) movements.push_back(Movement(position, position+Vector(-1, 1)));
}
// Jeżeli to ruch białego pionka
if(getPawn(position)->getColor() == CL_WHITE)
{
// Zabezpieczenie przed przekroczeniem indeksów
if(getPawn(position)->getPosition().x > 1 &&
getPawn(position)->getPosition().y > 1)
// Jeżeli miejsce 1 jest wolne, wrzuć do listy
// Jeżeli ruch w górę-lewo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x >= 1 && position.y >= 1)
if(!getPawn(position+Vector(-1, -1))) movements.push_back(Movement(position, position+Vector(-1, -1)));
// Jeżeli miejsce 2 jest wolne, wrzuć do listy
if(position.y > 1)
// Jeżeli ruch w górę-prawo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x < TILES_COUNT-1 && position.y >= 1)
if(!getPawn(position+Vector(1, -1))) movements.push_back(Movement(position, position+Vector(1, -1)));
// Jeżeli ruch w dół-lewo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x >= 1 && position.y < TILES_COUNT-1)
if(!getPawn(position+Vector(-1, 1))) movements.push_back(Movement(position, position+Vector(-1, 1)));
// Jeżeli ruch w dół-prawo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x < TILES_COUNT-1 && position.y < TILES_COUNT-1)
if(!getPawn(position+Vector(1, 1))) movements.push_back(Movement(position, position+Vector(1, 1)));
// Jeżeli ruch w górę jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.y >= 1)
if(!getPawn(position+Vector(0, -1))) movements.push_back(Movement(position, position+Vector(0, -1)));
// Jeżeli ruch w dół jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.y < TILES_COUNT-1)
if(!getPawn(position+Vector(0, 1))) movements.push_back(Movement(position, position+Vector(0, 1)));
// Jeżeli ruch w lewo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x >= 1)
if(!getPawn(position+Vector(-1, 0))) movements.push_back(Movement(position, position+Vector(-1, 0)));
// Jeżeli ruch w prawo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x < TILES_COUNT-1)
if(!getPawn(position+Vector(1, 0))) movements.push_back(Movement(position, position+Vector(1, 0)));
}
// Jeżeli jest to zwykły pionek
else
{
// Jeżeli to ruch białego pionka
if(getPawn(position)->getColor() == CL_WHITE)
{
// Jeżeli ruch w górę-lewo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x >= 1 && position.y >= 1)
if(!getPawn(position+Vector(-1, -1))) movements.push_back(Movement(position, position+Vector(-1, -1)));
// Jeżeli ruch w górę-prawo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x < TILES_COUNT-1 && position.y >= 1)
if(!getPawn(position+Vector(1, -1))) movements.push_back(Movement(position, position+Vector(1, -1)));
}
// Jeżeli to ruch czarnego pionka
else
{
// Jeżeli ruch w dół-lewo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x >= 1 && position.y < TILES_COUNT-1)
if(!getPawn(position+Vector(-1, 1))) movements.push_back(Movement(position, position+Vector(-1, 1)));
// Jeżeli ruch w dół-prawo jest możliwy (jest puste miejsce) dorzuć ruch do listy
if(position.x < TILES_COUNT-1 && position.y < TILES_COUNT-1)
if(!getPawn(position+Vector(1, 1))) movements.push_back(Movement(position, position+Vector(1, 1)));
}
}
return movements;
}
std::list<Movement> Board::getPossibleMovements(Color color)
std::list<Movement> Board::getPossibleGlobalBeatings(Color color)
{
std::list<Movement> movements;
// Dla każdych pionków
for(int x = 0; x < TILES_COUNT; ++x)
for(int y = 0; y < TILES_COUNT; ++y)
{
// Jeżeli to jest pionek
if(board[x][y])
{
// Jeżeli jest to pionek o który nam chodzi
if(board[x][y]->getColor() == color)
// Dodaj wszystkie jego możliwe ruchy do listy ruchów
for(auto m: getPossibleBeatings(Vector(x, y))) movements.push_back(m);
}
}
return movements;
}
std::list<Movement> Board::getPossibleGlobalMovements(Color color)
{
std::list<Movement> movements;
// Jeżeli istnieją bicia zwracamy tylko je
if(arePossibleGlobalBeatings(color)) return getPossibleGlobalBeatings(color);
// Dla każdych pionków
for(int x = 0; x < TILES_COUNT; ++x)
for(int y = 0; y < TILES_COUNT; ++y)

View File

@@ -129,8 +129,8 @@ void Game::executePlayerRound(sf::Vector2f mouse_position)
// Pobieramy informacje o pionku(lub jego braku) z interesującego nas pola
Pawn* ptr = board.getPawn(position);
// Jeżeli tura należy do gracza, pionek istnieje i należy do gracza oraz ruch nie został jeszcze rozpoczęty:
if((round == getPlayerColor()) && ptr && (ptr->getColor() == getPlayerColor()) && (!movements_sequence))
// Jeżeli tura należy do gracza, pionek istnieje i należy do gracza
if((round == getPlayerColor()) && ptr && (ptr->getColor() == getPlayerColor()))
{
// Zaznaczamy pionek i pobieramy do niego wskaźnik
ptr = board.selectPawn(Vector(position));

View File

@@ -1,10 +1,10 @@
#include "../inc/minimax.hh"
int MiniMax::f1(Board& board, const Color& color)
int MiniMax::evaluate(Board& board, const Color& color)
{
// Wycena
int value = 0, tmp_value;
int value = 0, pawn_value;
// Wskaźnik na aktualnie oceniany pionek
Pawn* ptr = NULL;
@@ -19,27 +19,36 @@ int MiniMax::f1(Board& board, const Color& color)
if(ptr)
{
// Jeżeli pionek jest damką:
if(ptr->isQueen()) tmp_value = 10;
else tmp_value = 1;
if(ptr->isQueen()) pawn_value = 10;
else pawn_value = 1;
// Jeżeli pionek należy do obszaru III -- waga 3
if(((ptr->getPosition().x >= 3) && (ptr->getPosition().y >= 3)) &&
((ptr->getPosition().x < (TILES_COUNT-3)) && (ptr->getPosition().y < (TILES_COUNT-3))))
tmp_value *= 3;
/*** ZASADA TRZECH OBSZARÓW ***/
{
// Jeżeli pionek należy do obszaru III -- waga 1
if(((ptr->getPosition().x >= 3) && (ptr->getPosition().y >= 3)) &&
((ptr->getPosition().x < (TILES_COUNT-3)) && (ptr->getPosition().y < (TILES_COUNT-3))))
; // więc nie ruszamy
// Jeżeli pionek należy do obszaru II -- waga 2
else if(((ptr->getPosition().x >= 2) && (ptr->getPosition().y >= 2)) &&
((ptr->getPosition().x < (TILES_COUNT-2)) && (ptr->getPosition().y < (TILES_COUNT-2))))
tmp_value *= 2;
// Jeżeli pionek należy do obszaru II -- waga 2
else if(((ptr->getPosition().x >= 2) && (ptr->getPosition().y >= 2)) &&
((ptr->getPosition().x < (TILES_COUNT-2)) && (ptr->getPosition().y < (TILES_COUNT-2))))
pawn_value *= 2;
// W przeciwnym wypadku, zostaje waga 1
// W przeciwnym wypadku, zostaje waga 1
else
pawn_value *= 3;
}
/*** MOŻLIWOŚĆ BICIA ***/
if(board.isPossibleBeating(ptr->getPosition())) pawn_value += 5;
// Jeżeli NIE jest to pionek należący do gracza zdefiniowanego kolorem color
// przemnażamy przez -1
if(ptr->getColor() != color) tmp_value *= -1;
if(ptr->getColor() != color) pawn_value *= -1;
// Dodajemy tmp_value do wyceny
value += tmp_value;
// Dodajemy pawn_value do wyceny
value += pawn_value;
}
}
@@ -73,7 +82,7 @@ int MiniMax::alphabeta(Board board, int depth, int alpha, int beta, Movement& be
if(color != AI_COLOR)
{
// Dla każdego potomka
for(auto& m: board.getPossibleMovements(color))
for(auto& m: board.getPossibleGlobalMovements(color))
{
// Tworzymy nowy stan gry
Board new_board(board);
@@ -93,7 +102,7 @@ int MiniMax::alphabeta(Board board, int depth, int alpha, int beta, Movement& be
else
// Dla każdego potomka
for(auto& m: board.getPossibleMovements(color))
for(auto& m: board.getPossibleGlobalMovements(color))
{
// Tworzymy nowy stan gry
Board new_board(board);