25.2. Kolory, figury i pola
W grze w szachy bierze udział dwójka graczy, z których jeden gra figurami białymi, a drugi czarnymi. W pliku Color.scala znajdują się definicje klasy Color oraz obiektów White i Black, reprezentujących oba kolory.
Plik Color.scala: package chess abstract sealed class Color { def other: Color def firstRow: Int } case object White extends Color { def other = Black def firstRow = 1 } case object Black extends Color { def other = White def firstRow = 8 }
Pliki źródłowe umieścimy w podkatalogu src/main/scala naszego projektu. Jest to jedno z miejsc, w którym sbt standardowo szuka plików źródłowych języka Scala. Poleceniem compile możemy skompilować nasz plik (przy okazji sbt pobiera kolejne potrzebne zależności).
> compile
[info] Updating {file:/H:/jps2/examples/chess/}chess...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] downloading https://jcenter.bintray.com/org/scala-lang/scala-library/2.11.7/scala-library-2.11.7.jar ...
[info] [SUCCESSFUL ] org.scala-lang#scala-library;2.11.7!scala-library.jar (21821ms)
[info] downloading https://jcenter.bintray.com/org/scala-lang/scala-compiler/2.11.7/scala-compiler-2.11.7.jar ...
…
[info] Done updating.
[info] Compiling 1 Scala source to H:\jps2\examples\chess\target\scala-2.11\classes...
[info] 'compiler-interface' not yet compiled for Scala 2.11.7. Compiling...
[info] Compilation completed in 53.443 s
[success] Total time: 171 s, completed 2016-02-26 13:35:41
>
Z sesji sbt możemy uruchomić sesję konsoli języka Scala za pomocą polecenia console.
> console [info] Starting scala interpreter... [info] Welcome to Scala version 2.11.7 (Java HotSpot(TM) Client VM, Java 1.8.0_05). Type in expressions to have them evaluated. Type :help for more information. scala>
W klasie Color zadeklarowana jest metoda other, zaimplementowana w obu obiektach dziedziczących z tej klasy, która pozwala uzyskać dostęp do obiektu przeciwnego koloru.
scala> import chess._ import chess._ scala> White.other res0: chess.Black.type = Black scala> Black.other res1: chess.White.type = White
Ponadto w tej klasie zadeklarowana jest metoda firstRow, zwracająca numer pierwszego wiersza planszy, z punktu widzenia określonego gracza.
scala> White.firstRow res2: Int = 1 scala> Black.firstRow res3: Int = 8
W grze w szachy używanych jest sześć różnych figur w każdym kolorze. Są to król (ang. King), hetman (ang. Queen), wieża (ang. Rook), goniec (ang. Bishop), skoczek (ang. Knight) oraz pion (ang. Pawn). Plik Figure.scala przedstawia w jaki sposób te figury są reprezentowane w programie.
Plik Figure.scala: package chess abstract sealed class FigureType case object King extends FigureType case object Queen extends FigureType case object Rook extends FigureType case object Bishop extends FigureType case object Knight extends FigureType case object Pawn extends FigureType case class Figure(figureType: FigureType, figureColor: Color) { override def toString = (figureType, figureColor) match { case (King, White) => "k" case (Queen, White) => "q" case (Rook, White) => "r" case (Bishop, White) => "b" case (Knight, White) => "n" case (Pawn, White) => "p" case (King, Black) => "K" case (Queen, Black) => "Q" case (Rook, Black) => "R" case (Bishop, Black) => "B" case (Knight, Black) => "N" case (Pawn, Black) => "P" } def unicodeSymbol = (figureType, figureColor) match { case (King, White) => "\u2654" case (Queen, White) => "\u2655" case (Rook, White) => "\u2656" case (Bishop, White) => "\u2657" case (Knight, White) => "\u2658" case (Pawn, White) => "\u2659" case (King, Black) => "\u265a" case (Queen, Black) => "\u265b" case (Rook, Black) => "\u265c" case (Bishop, Black) => "\u265d" case (Knight, Black) => "\u265e" case (Pawn, Black) => "\u265f" } }
Klasa FigureType i dziedziczące z niej obiekty reprezentują różne rodzaje figur szachowych. Klasa Figure reprezentuje natomiast figury określonego rodzaju i koloru. Figure jest klasą przypadku mającą elementy figureType i figureColor, reprezentujące właśnie rodzaj i kolor figury. Metoda toString tej klasy zwraca jednoliterowy napis odpowiadający danej figurze. Litera reprezentująca figurę to ta litera alfabetu, od jakiej rozpoczyna się angielska nazwa figury, z wyjątkiem skoczka, w którego przypadku wykorzystana jest druga litera, gdyż pierwsza jest taka sama, jak w przypadku króla. Figury białe reprezentowane są przez małe litery, a figury czarne przez duże. Metoda unicodeSymbol zwraca symbol Unicode odpowiadający danej figurze.
scala> Figure(King, White) res0: chess.Figure = k
scala> Figure(King, Black) res1: chess.Figure = K scala> Figure(Knight, White) res2: chess.Figure = n
Gra w szachy odbywa się na planszy mającej 64 pola. Plansza ma kształt kwadratu mającego osiem rzędów — oznaczanych numerami od 1 do 8 — i osiem kolumn — oznaczanych literami od a do h. W pliku Field.scala znajduje się definicja klasy Field, reprezentującej pole planszy.
Plik Field.scala: package chess case class Field(col: Int, row: Int) { override def toString = (col + 'a' - 1).toChar.toString + row def relative(c: Int, r: Int) = Field(col+c, row+r) def isLastRow(color: Color) = row == color.other.firstRow def isValid = col >= 1 && col <= 8 && row >= 1 && row <= 8 }
Klasa Field jest klasą przypadku mającą zdefiniowane dwa elementy, reprezentujące kolumnę i wiersz, czyli współrzędne pola na planszy. Każda ze współrzędnych mieści się w zakresie od 1 do 8. Metoda toString jest zdefiniowana w taki sposób, aby zwracać współrzędne pola w postaci litery określającej kolumnę i liczby określającej wiersz.
scala> Field(1,1) res3: chess.Field = a1 scala> Field(2,1) res4: chess.Field = b1 scala> Field(8,8) res5: chess.Field = h8
Klasa Field posiada dodatkowe metody, które są wykorzystywane w innych fragmentach programu. Metoda relative zwraca nowe pole, mające współrzędne przesunięte w stosunku do danego pola o określoną liczbę kolumn i wierszy. Metoda isLastRow zwraca wartość logiczną informującą, czy dane pole należy do ostatniego rzędu z punktu widzenia określonego gracza. Metoda isValid zwraca wartość logiczną informującą, czy pole ma prawidłowe współrzędne, czyli czy należy do planszy.
scala> Field(2,3).relative(1,1) res6: chess.Field = c4 scala> Field(2,3).relative(4,5) res7: chess.Field = f8 scala> Field(7,8).isLastRow(Black) res8: Boolean = false scala> Field(7,1).isLastRow(Black) res9: Boolean = true scala> Field(2,2).isValid res10: Boolean = true scala> Field(0,2).isValid res11: Boolean = false
Plik Color.scala:
package chess
abstract sealed class Color {
def other: Color
def firstRow: Int
}
case object White extends Color {
def other = Black
def firstRow = 1
}
case object Black extends Color {
def other = White
def firstRow = 8
}
