25.7. Ocena sytuacji na planszy
Ponieważ opisywana aplikacja ma potrafić grać w szachy z człowiekiem, więc potrzebna jest implementacja algorytmu określającego jaki następny ruch ma zostać wykonany przez komputer. Z kolei taki algorytm porzebuje sposobu oceny różnych stanów gry. Algorytm oceniający jest zaimplementowany w pliku Rank.scala. Plik zawiera definicję klasy Rank i jej obiektu towarzyszącego.
Plik Rank.scala: package chess import language.implicitConversions object Rank { def figureRank(figure: Figure) = figure.figureType match { case Queen => 900 case Rook => 450 case Knight | Bishop => 300 case Pawn => 100 case _ => 0 } def fieldRank(field: Field) = { def colRowRank(cr: Int) = if (cr>=5) 9-cr else cr 2*colRowRank(field.col) * colRowRank(field.row) } implicit def Game2Rank(game: Game): Rank = new Rank(game) } class Rank(game: Game) { import Rank._ import FigureMoves._ def figureDefendingOtherFiguresRank(field:Field, figure:Figure) = game.sameColorDestinations(figure.figureColor, figureMoves(figure,field,true)).size/2 def checkRank(color: Color) = if (game.color == color.other && game.isKingUnderCheck) 50 else 0 def colorRank(color: Color) = (for ((field, figure) <- game.board.iterator if figure.figureColor == color; r1 = figureRank(figure); r2 = fieldRank(field); r3 = game.figureDefendingOtherFiguresRank(field, figure)) yield r1 + r2 + r3).sum + game.checkRank(color) def rank(color: Color) = game.colorRank(color)-game.colorRank(color.other) }
Metoda rank klasy Rank zwraca liczbę całkowitą reprezentującą ocenę stanu gry z punktu widzenia określonego gracza. Im większa jest ta liczba, tym lepsza jest ocena danej sytuacji na planszy. Klasa Rank ma parametr game typu Game, który reprezentuje oceniany stan gry. Dzięki niejawnej konwersji obiektów typu Game na obiekty typu Rank, zdefiniowanej w wierszu , możliwe jest wywoływanie metody rank na obiektach typu Game.
scala> import chess._, Rank._ import chess._ import Rank._ scala> GameStart.rank(White) res0: Int = 0
Metoda rank zwraca różnicę punktów otrzymanych dzięki wywołaniom metody colorRank, oceniającej sytuację na planszy z punktu widzenia konkretnego gracza. Metoda działa w ten sposób, że przypisuje punktację każdej z figur danego koloru i dodaje te punkty, a do tak uzyskanej sumy dodaje jeszcze wynik metody checkRank. Punkty przyznawane są każdej z figur według trzech kryteriów, ocenianych przez metody figureRank, fieldRank oraz figureDefendingOtherFiguresRank. Metoda figureRank przypisuje każdej z figur określoną punktację, zależną od rodzaju figury. Najlepiej oceniany jest hetman, później wieża, następnie równo skoczek i goniec, a najmniej pion. Król nie otrzymuje punktów, gdyż i tak każda ze stron ma przez cały czas trwania partii po jednym i tylko jednym królu.
scala> figureRank(Figure(Queen,White)) res1: Int = 900 scala> figureRank(Figure(Knight,Black)) res2: Int = 300
Metoda fieldRank przypisuje punktację w zależności od pozycji figury na planszy, przy czym lepiej są oceniane pola w centrum planszy, a gorzej pola dalsze od centrum.
scala> fieldRank(Field(1,1)) res3: Int = 2 scala> fieldRank(Field(2,5)) res4: Int = 16
scala> fieldRank(Field(4,4)) res5: Int = 32
Metoda figureDefendingOtherFiguresRank przypisuje punktację zależną od tego, ilu figur tego samego koloru broni dana figura. Im więcej figur jest bronionych, tym lepiej.
scala> GameStart.figureDefendingOtherFiguresRank(Field(4,1),Figure(Queen,White)) res6: Int = 2 scala> GameStart.figureDefendingOtherFiguresRank(Field(4,7),Figure(Pawn,Black)) res7: Int = 0
Metoda checkRank przypisuje dodatkowe punkty, jeśli król przeciwnika jest szachowany.
scala> GameStart.checkRank(White) res8: Int = 0 scala> val g = GameStart.move(Field(7,2),Field(7,4),None).get. | move(Field(5,7),Field(5,6),None).get. | move(Field(6,2),Field(6,4),None).get. | move(Field(4,8),Field(8,4),None).get g: chess.OngoingGame = Last move: Black d8 to h4 abcdefgh 8RNB.KBNR8 7PPPP.PPP7 6....P...6 5........5 4.....ppQ4 3........3 2ppppp..p2 1rnbqkbnr1 abcdefgh scala> g.checkRank(Black) res9: Int = 50