From 403fdc707cf011ff184534d1d332238e89fde826 Mon Sep 17 00:00:00 2001 From: FelixL <50499590+SlaynAndKorpil@users.noreply.github.com> Date: Sun, 3 May 2020 20:12:10 +0200 Subject: [PATCH 1/4] Refactoring of Square chaos into simpler Sqr --- console/src/console/InputInterpreter.scala | 4 +- framework/src/framework/BoardMap.scala | 133 ++++++---------- framework/src/framework/BoardMeta.scala | 2 +- .../BoardStatus/GameStatus/GameStatus.scala | 4 +- framework/src/framework/ChessBoard.scala | 84 +++++----- framework/src/framework/ChessIO.scala | 2 +- framework/src/framework/Column.scala | 85 ---------- .../src/framework/IOEvents/IOEvent.scala | 6 +- framework/src/framework/Input/Input.scala | 4 +- framework/src/framework/MoveData.scala | 10 +- framework/src/framework/Positions.scala | 2 +- framework/src/framework/SaveLoader.scala | 16 +- framework/src/framework/Sqr.scala | 84 ++++++++++ framework/src/framework/Square.scala | 147 ------------------ .../src/framework/javaInterfacing/JInput.java | 4 +- .../pathfinding/BooleanPathfinder.scala | 6 +- .../pathfinding/KingMovementPathfinder.scala | 14 +- .../MonoDirectionalPathfinder.scala | 4 +- .../framework/pathfinding/Pathfinder.scala | 12 +- .../pathfinding/TerminableOnce.scala | 22 +-- .../TripleDirectionalPathfinder.scala | 10 +- graphics/src/graphics/Board.scala | 4 +- graphics/src/graphics/BoardEventHandler.scala | 2 +- graphics/src/graphics/SquareButton.scala | 2 +- 24 files changed, 236 insertions(+), 427 deletions(-) delete mode 100644 framework/src/framework/Column.scala create mode 100644 framework/src/framework/Sqr.scala delete mode 100644 framework/src/framework/Square.scala diff --git a/console/src/console/InputInterpreter.scala b/console/src/console/InputInterpreter.scala index ed148c2..82f259f 100644 --- a/console/src/console/InputInterpreter.scala +++ b/console/src/console/InputInterpreter.scala @@ -267,8 +267,8 @@ class InputInterpreter extends ChessIO with CommandRegistrator { * Parses a move command. */ def parseMove(move: String): CommandResult = { - val from = Square(move.head, move(1).asDigit) - val to = Square(move(2), move(3).asDigit) + val from = Sqr(move.head, move(1).asDigit) + val to = Sqr(move(2), move(3).asDigit) val moveInput = MoveParams(from, to) receiveInput(moveInput) NoMessage diff --git a/framework/src/framework/BoardMap.scala b/framework/src/framework/BoardMap.scala index 8d775a3..e719fc8 100644 --- a/framework/src/framework/BoardMap.scala +++ b/framework/src/framework/BoardMap.scala @@ -1,6 +1,11 @@ package framework -import scala.collection.AbstractMap +import framework.ChessBoard.columnLetter + +import scala.collection.IndexedSeqLike +import scala.collection.generic.CanBuildFrom +import scala.collection.mutable.ArrayBuffer +import scala.xml._ /** * A chess board. Stores pieces in a 2 dimensional system (columns and rows). @@ -8,77 +13,21 @@ import scala.collection.AbstractMap * @author Felix Lehner * @version */ -final case class BoardMap(key1: Char, value1: Column, - key2: Char, value2: Column, - key3: Char, value3: Column, - key4: Char, value4: Column, - key5: Char, value5: Column, - key6: Char, value6: Column, - key7: Char, value7: Column, - key8: Char, value8: Column) - extends AbstractMap[Char, Column] with Map[Char, Column] with Serializable { +final case class BoardMap(ps: Array[Array[Piece]]) + extends IndexedSeq[IndexedSeq[Piece]] with IndexedSeqLike[IndexedSeq[Piece], BoardMap] { + + override val length = 8 override def size = 8 - override def apply(key: Char): Column = - if (key == key1) value1 - else if (key == key2) value2 - else if (key == key3) value3 - else if (key == key4) value4 - else if (key == key5) value5 - else if (key == key6) value6 - else if (key == key7) value7 - else if (key == key8) value8 - else throw new NoSuchElementException("key not found: " + key) - - override def contains(key: Char): Boolean = - (key == key1) || (key == key2) || (key == key3) || (key == key4) || (key == key5) || (key == key6) || (key == key7) || (key == key7) || (key == key8) - - def get(key: Char): Option[Column] = - if (key == key1) Some(value1) - else if (key == key2) Some(value2) - else if (key == key3) Some(value3) - else if (key == key4) Some(value4) - else if (key == key5) Some(value5) - else if (key == key6) Some(value6) - else if (key == key7) Some(value7) - else if (key == key8) Some(value8) - else None - - def iterator = Iterator((key1, value1), (key2, value2), (key3, value3), (key4, value4), (key5, value5), (key6, value6), (key7, value7), (key8, value8)) - - def +[V1 >: Column](kv: (Char, V1)): Map[Char, V1] = throw new UnsupportedOperationException("size of this map limited to 8") - - def updated(key: Char, value: Column): BoardMap = - if (key == key1) new BoardMap(key1, value, key2, value2, key3, value3, key4, value4, key5, value5, key6, value6, key7, value7, key8, value8) - else if (key == key2) new BoardMap(key1, value1, key2, value, key3, value3, key4, value4, key5, value5, key6, value6, key7, value7, key8, value8) - else if (key == key3) new BoardMap(key1, value1, key2, value2, key3, value, key4, value4, key5, value5, key6, value6, key7, value7, key8, value8) - else if (key == key4) new BoardMap(key1, value1, key2, value2, key3, value3, key4, value, key5, value5, key6, value6, key7, value7, key8, value8) - else if (key == key5) new BoardMap(key1, value1, key2, value2, key3, value3, key4, value4, key5, value, key6, value6, key7, value7, key8, value8) - else if (key == key6) new BoardMap(key1, value1, key2, value2, key3, value3, key4, value4, key5, value5, key6, value, key7, value7, key8, value8) - else if (key == key7) new BoardMap(key1, value1, key2, value2, key3, value3, key4, value4, key5, value5, key6, value6, key7, value, key8, value8) - else if (key == key8) new BoardMap(key1, value1, key2, value2, key3, value3, key4, value4, key5, value5, key6, value6, key7, value7, key8, value) - else throw new NoSuchElementException("key not found: " + key) - - def -(key: Char): Map[Char, Column] = throw new UnsupportedOperationException("size of this map has to be 8") - - override def foreach[U](f: ((Char, Column)) => U): Unit = { - f((key1, value1)) - f((key2, value2)) - f((key3, value3)) - f((key4, value4)) - f((key5, value5)) - f((key6, value6)) - f((key7, value7)) - f((key8, value8)) - } + val pieces: Array[Array[Piece]] = if (ps.length == 8 && ps.forall(_.length == 8)) ps else Array.fill(8)(Array.fill(8)(NoPiece)) /** * @param sqr coordinates of the wanted piece * @return the chess square at a specific position on the board, [[scala.None]] if the sqr does not exist */ - def getPiece(sqr: Square): Option[Piece] = - if (sqr.isValid) Some(apply(sqr._1).pieceAt(sqr._2)) + def getPiece(sqr: Sqr): Option[Piece] = + if (sqr.isValid) Some(apply(sqr._1)(sqr._2)) else None /** @@ -89,7 +38,7 @@ final case class BoardMap(key1: Char, value1: Column, * @return the piece at some specified position */ @inline - def apply(sqr: Square): Piece = getPiece(sqr) getOrElse NoPiece + def apply(sqr: Sqr): Piece = getPiece(sqr) getOrElse NoPiece /** * Updates the board. @@ -98,9 +47,11 @@ final case class BoardMap(key1: Char, value1: Column, * @param piece the piece the square shall be updated to * @return a ChessBoard with updated squares. */ - def updated(square: Square, piece: Piece): BoardMap = { - val updated = this(square._1).updated(square._2, piece) - this.updated(square._1, updated) + def updated(square: Sqr, piece: Piece): BoardMap = { + if (square.isValid) { + val updated = this(square._1).updated(square._2, piece) + this.updated(square._1, updated) + } else this } /** @@ -108,7 +59,7 @@ final case class BoardMap(key1: Char, value1: Column, * * @param sqr the square to be emptied */ - def emptySquare(sqr: Square): BoardMap = + def emptySquare(sqr: Sqr): BoardMap = if (sqr.isValid) updated(sqr, NoPiece) else this @@ -121,18 +72,18 @@ final case class BoardMap(key1: Char, value1: Column, * @param piece the moving piece * @return the board after the move */ - def movePiece(from: Square, to: Square, piece: Piece): BoardMap = { + def movePiece(from: Sqr, to: Sqr, piece: Piece): BoardMap = { var result = this //testing for en passant and castling piece match { case Pawn(_, _) if apply(to).isEmpty && from.column != to.column => - result = result.emptySquare(Square(to.column, from.row)) + result = result.emptySquare(Sqr(to.column, from.row)) case King(color, moved) if !moved && (to.column == 'c' || to.column == 'g') => val row = ChessBoard.ClassicalValues.piecesStartLine(color) val col = if (to.column == 'c') 'd' else 'f' val emptyCol = if (to.column == 'c') 'a' else 'h' - result = result.updated(Square(col, row), Rook(color)).emptySquare(Square(emptyCol, row)) + result = result.updated(Sqr(col, row), Rook(color)).emptySquare(Sqr(emptyCol, row)) case _ => } @@ -140,6 +91,20 @@ final case class BoardMap(key1: Char, value1: Column, result.updated(to, resPiece).emptySquare(from) } + def toXml: NodeSeq = { + for (x <- 1 to 8; col = columnLetter(x)) yield {squares(col).saveData} copy (label = col.toUpper toString) + + // in Column: + /** + * Saves this column as xml. + */ + def saveData: IndexedSeq[scala.xml.Elem] = { + var result: IndexedSeq[scala.xml.Elem] = IndexedSeq() + pieces.indices.foreach(i => if (pieces(i).nonEmpty) result = result :+ pieces(i).toXml.copy(label = "l" + (i + 1))) + result + } + } + /** * Formats the board as a [[String]]. * @@ -155,19 +120,17 @@ final case class BoardMap(key1: Char, value1: Column, object BoardMap { - def apply(from: Map[Char, Column]): BoardMap = - if (from.size >= 8) { - val seq = from.toSeq - BoardMap(seq(0), seq(1), seq(2), seq(3), seq(4), seq(5), seq(6), seq(7)) - } - else throw new UnsupportedOperationException("size of this map has to be 8") + def apply(pieces: Seq[Piece]*): BoardMap = fromSeq(pieces) + + def fromSeq(buf: Seq[Seq[Piece]]): BoardMap = new BoardMap(buf.toArray.map(_.toArray)) - def apply(from: scala.IndexedSeq[(Char, Column)]): BoardMap = - if (from.size >= 8) BoardMap(from(0), from(1), from(2), from(3), from(4), from(5), from(6), from(7)) - else throw new UnsupportedOperationException("size of this map has to be 8") + def newBuilder: scala.collection.mutable.Builder[Seq[Piece], BoardMap] = new ArrayBuffer mapResult fromSeq - def apply(kv1: (Char, Column), kv2: (Char, Column), kv3: (Char, Column), kv4: (Char, Column), kv5: (Char, Column), - kv6: (Char, Column), kv7: (Char, Column), kv8: (Char, Column)): BoardMap = - new BoardMap(kv1._1, kv1._2, kv2._1, kv2._2, kv3._1, kv3._2, kv4._1, kv4._2, - kv5._1, kv5._2, kv6._1, kv6._2, kv7._1, kv7._2, kv8._1, kv8._2) + implicit def canBuildFrom: CanBuildFrom[BoardMap, Seq[Piece], BoardMap] = + new CanBuildFrom[BoardMap, Seq[Piece], BoardMap] { + + def apply(): scala.collection.mutable.Builder[Seq[Piece], BoardMap] = newBuilder + + def apply(from: BoardMap): scala.collection.mutable.Builder[Seq[Piece], BoardMap] = newBuilder + } } diff --git a/framework/src/framework/BoardMeta.scala b/framework/src/framework/BoardMeta.scala index e82fcec..8a44b81 100644 --- a/framework/src/framework/BoardMeta.scala +++ b/framework/src/framework/BoardMeta.scala @@ -69,7 +69,7 @@ trait BoardMeta { if (promoPiece != NoPiece) { gameStatus match { - case PromoReq(sqr: Square) => + case PromoReq(sqr: Sqr) => val updatedHistory = PromotionMove(history.head.startPos, history.head.piece, sqr, history.head.captured, promoPiece) :: this.history.tail val result = updated(sqr, promoPiece).clone(gameStatus = StandardReq, history = updatedHistory) diff --git a/framework/src/framework/BoardStatus/GameStatus/GameStatus.scala b/framework/src/framework/BoardStatus/GameStatus/GameStatus.scala index 048c5a7..c605d39 100644 --- a/framework/src/framework/BoardStatus/GameStatus/GameStatus.scala +++ b/framework/src/framework/BoardStatus/GameStatus/GameStatus.scala @@ -32,7 +32,7 @@ object GameStatus { else if (state == StandardReq.toString) Right(StandardReq) else if (contains(state, "PromoReq(", ")")) { val sqr = state.substring(9, last) - val result = Square(sqr) + val result = Sqr(sqr) result match { case Some(square) => Right(PromoReq(square)) case _ => failMessage @@ -60,7 +60,7 @@ case object StandardReq extends Active { } /** Waiting for a promotion */ -case class PromoReq(on: Square) extends Active +case class PromoReq(on: Sqr) extends Active /** Waiting for an answer to a draw offer. */ case object DrawAcceptanceReq extends Active diff --git a/framework/src/framework/ChessBoard.scala b/framework/src/framework/ChessBoard.scala index 5a95167..baeda03 100644 --- a/framework/src/framework/ChessBoard.scala +++ b/framework/src/framework/ChessBoard.scala @@ -34,16 +34,16 @@ case class ChessBoard( import ChessBoard._ /** All pieces (excluding [[framework.NoPiece NoPiece]]s) and their position */ - lazy val allPieces: Array[(Square, AnyPiece)] = { + lazy val allPieces: Array[(Sqr, AnyPiece)] = { val allSquares = for { x <- 1 to 8 col = columnLetter(x) row <- 1 to 8 - square = Square(col, row) + square = Sqr(col, row) piece: Piece = apply(square) if piece.nonEmpty } yield (square, piece) - allSquares.toArray.asInstanceOf[Array[(Square, AnyPiece)]] + allSquares.toArray.asInstanceOf[Array[(Sqr, AnyPiece)]] } /** @@ -115,12 +115,12 @@ case class ChessBoard( /** * Applies a function to all pieces and returns the results. */ - def mapPiece[T](func: (Square, AnyPiece) => T): IndexedSeq[T] = + def mapPiece[T](func: (Sqr, AnyPiece) => T): IndexedSeq[T] = for { col <- 1 to 8 column = columnLetter(col) row <- 1 to 8 - square = Square(column, row) + square = Sqr(column, row) piece = apply(square) if piece.nonEmpty } yield func(square, piece.asInstanceOf[AnyPiece]) @@ -133,14 +133,14 @@ case class ChessBoard( * @return the piece at some specified position */ @inline - def apply(sqr: Square): Piece = getPiece(sqr) getOrElse NoPiece + def apply(sqr: Sqr): Piece = getPiece(sqr) getOrElse NoPiece /** * @param sqr coordinates of the wanted piece * @return the chess square at a specific position on the board, [[scala.None]] if the sqr does not exist */ @inline - def getPiece(sqr: Square): Option[Piece] = squares.getPiece(sqr) + def getPiece(sqr: Sqr): Option[Piece] = squares.getPiece(sqr) /** * Filters for all pieces that match a predicate. @@ -207,7 +207,7 @@ case class ChessBoard( * * @param sqr the square to be emptied */ - def emptySquare(sqr: Square): ChessBoard = clone(squares = squares.emptySquare(sqr)) + def emptySquare(sqr: Sqr): ChessBoard = clone(squares = squares.emptySquare(sqr)) /** * Moves a piece after testing for validity of the move which depends on the following aspects: @@ -225,7 +225,7 @@ case class ChessBoard( * @param to the end-coordinates * @return the updated board */ - def move(from: Square, to: Square): Option[Output] = if (from.isValid && to.isValid) { + def move(from: Sqr, to: Sqr): Option[Output] = if (from.isValid && to.isValid) { val movingPiece = apply(from) val endPiece = apply(to) val startColor = movingPiece.color @@ -283,7 +283,7 @@ case class ChessBoard( * @tparam T some return type * @return either the result of `func` or `onFailure` */ - def doOnCheck[T](func: Square => T, onFailure: T, color: AnyColor = turn): IndexedSeq[T] = { + def doOnCheck[T](func: Sqr => T, onFailure: T, color: AnyColor = turn): IndexedSeq[T] = { val sqrs = checkedSquares() if (sqrs nonEmpty) sqrs map func else IndexedSeq(onFailure) @@ -295,11 +295,11 @@ case class ChessBoard( * @param color kings of this color are tested * @return a list of all positions with checked kings */ - def checkedSquares(color: AnyColor = turn): IndexedSeq[Square] = { + def checkedSquares(color: AnyColor = turn): IndexedSeq[Sqr] = { for { c <- 1 to 8 row <- 1 to 8 - sqr = Square(columnLetter(c), row) + sqr = Sqr(columnLetter(c), row) piece = apply(sqr) if piece === King(color) && isAttacked(sqr, withKing = true) } yield sqr @@ -315,7 +315,7 @@ case class ChessBoard( * @param endPiece the captured piece or [[framework.NoPiece NoPiece]] for an empty square * @return `true` if the move is legal, otherwise `false` */ - def isLegalMove(start: Square, end: Square, startPiece: Piece, endPiece: Piece): Boolean = { + def isLegalMove(start: Sqr, end: Sqr, startPiece: Piece, endPiece: Piece): Boolean = { val startCIndex = start.colIndx val endCIndex = end.colIndx val columnDif = endCIndex - startCIndex @@ -332,7 +332,7 @@ case class ChessBoard( val sPos = history.head.startPos val ePos = history.head.endPos piece === Pawn(color.opposite) && - sPos == Square(end.column, ClassicalValues.pawnStartLine(color.opposite)) && + sPos == Sqr(end.column, ClassicalValues.pawnStartLine(color.opposite)) && ePos == sPos + (0, -2 * direction) && lineDif == direction && (columnDif == 1 || columnDif == -1) && @@ -351,12 +351,12 @@ case class ChessBoard( //castle (!startPiece.moved && (end.column == 'c' || end.column == 'g') && { val rookCol = if (end.column == 'c') 'a' else 'h' - val rook = apply(Square(rookCol, ClassicalValues.piecesStartLine(color))) + val rook = apply(Sqr(rookCol, ClassicalValues.piecesStartLine(color))) val squaresToTest: List[NumericSquare] = - AbstractSqrCoordinate.sqr2indxSqr(Square(if (startCIndex < endCIndex) 'g' else 'c', start.row)) to start + AbstractSqrCoordinate.sqr2indxSqr(Sqr(if (startCIndex < endCIndex) 'g' else 'c', start.row)) to start def isSqrAttacked(sqr: AbstractSqrCoordinate[_]): Boolean = sqr match { - case square: Square => isAttacked(square, turn, withKing = true) + case square: Sqr => isAttacked(square, turn, withKing = true) case square: NumericSquare => isAttacked(square, turn, withKing = true) } @@ -373,7 +373,7 @@ case class ChessBoard( * @see [[framework.ChessBoard#isAttacked isAttacked]] * @return `true` if the square is attacked, otherwise `false` */ - def isAttacked(sqr: Square, withKing: Boolean): Boolean = { + def isAttacked(sqr: Sqr, withKing: Boolean): Boolean = { val attackedCol = apply(sqr).color attackedCol match { case col: AnyColor => isAttacked(sqr, col, withKing) @@ -388,7 +388,7 @@ case class ChessBoard( * @param attacked the 'defending' color * @return `true` if the square is attacked, otherwise `false` */ - def isAttacked(sqr: Square, attacked: AnyColor, withKing: Boolean): Boolean = + def isAttacked(sqr: Sqr, attacked: AnyColor, withKing: Boolean): Boolean = attackingPieces(sqr, attacked, withKing) > 0 /** @@ -399,7 +399,7 @@ case class ChessBoard( * @param withKing whether the king should be counted as possible attacker * @return `true` if the square is attacked, otherwise `false` */ - def attackingPieces(sqr: Square, attacked: AnyColor, withKing: Boolean = true): Int = { + def attackingPieces(sqr: Sqr, attacked: AnyColor, withKing: Boolean = true): Int = { implicit class Intersectable[P <: Piece](val content: Array[P]) { def ^[OtherP <: Piece](other: Array[OtherP]): Int = (for (i <- content; j <- other) yield if (j === i) 1 else 0) sum } @@ -442,7 +442,7 @@ case class ChessBoard( attackedByKnight + attackedByKing + attackedDiagonally + attackedOrthogonally + attackedByPawn } - def attackingSquares(sqr: Square, attacked: AnyColor, withKing: Boolean = true): Array[(Square, AnyPiece)] = { + def attackingSquares(sqr: Sqr, attacked: AnyColor, withKing: Boolean = true): Array[(Sqr, AnyPiece)] = { val opponent = attacked.opposite allPieces .filter(_._2.color == opponent) // filter all pieces of the correct color @@ -454,7 +454,7 @@ case class ChessBoard( * Tests if there is a piece on a specific square that is pinned (it blocks an attack against the king) * and thereby cannot be moved. */ - def isPinnedPiece(square: Square): Boolean = getPiece(square) match { + def isPinnedPiece(square: Sqr): Boolean = getPiece(square) match { case Some(piece) => piece match { case NoPiece => false @@ -491,14 +491,14 @@ case class ChessBoard( movesSinceLastCapture / 2 > 50 && movesSinceLastPawnMove / 2 > 50 /** - * Tests if a certain [[framework.Square]] is blocked, + * Tests if a certain [[framework.Sqr]] is blocked, * i.e. the piece on the square cannot move. * When the square is empty, `false` is returned. */ - def isBlockedSquare(square: Square): Boolean = { + def isBlockedSquare(square: Sqr): Boolean = { val piece = apply(square) - def atOffset(off: (Int, Int)): Square = square + off + def atOffset(off: (Int, Int)): Sqr = square + off def offsetPiece(off: (Int, Int)) = getPiece(atOffset(off)) @@ -573,9 +573,9 @@ case class ChessBoard( .forall(isBlockedSquare) def kingsBlocked = { - val kings: Array[(Square, AnyPiece)] = allPieces filter (_._2.isInstanceOf[King]) reverse + val kings: Array[(Sqr, AnyPiece)] = allPieces filter (_._2.isInstanceOf[King]) reverse - def kingIsBlocked(kingOnSq: (Square, AnyPiece)): Boolean = { + def kingIsBlocked(kingOnSq: (Sqr, AnyPiece)): Boolean = { import framework.pathfinding.KingMovementPathfinder import framework.pathfinding.WaypointResult._ @@ -585,7 +585,7 @@ case class ChessBoard( val kingColor = king.color val pathfinder = new KingMovementPathfinder { - override def decision(pos: Square): WaypointResult.Value = getPiece(pos) match { + override def decision(pos: Sqr): WaypointResult.Value = getPiece(pos) match { case Some(piece) if !isAttacked(pos, kingColor, withKing = false) && kingColor != piece.color => piece.color match { case color: AnyColor if color == kingColor.opposite => Positive @@ -698,7 +698,7 @@ case class ChessBoard( * * @see [[framework.ChessBoard#isEmptyDiagonal isEmptyDiagonal]] */ - def isEmptyOrthogonal(from: Square, to: Square): Boolean = { + def isEmptyOrthogonal(from: Sqr, to: Sqr): Boolean = { val orthogonal = from._1 == to._1 || from._2 == to._2 orthogonal && isEmptyConnection(from, to) } @@ -709,7 +709,7 @@ case class ChessBoard( * * @see [[framework.ChessBoard#isEmptyOrthogonal isEmptyOrthogonal]] */ - def isEmptyDiagonal(from: Square, to: Square): Boolean = { + def isEmptyDiagonal(from: Sqr, to: Sqr): Boolean = { val startColIndex = from.colIndx val endColIndex = to.colIndx val diagonal = startColIndex - endColIndex == from._2 - to._2 || startColIndex - to._2 == endColIndex - from._2 @@ -723,7 +723,7 @@ case class ChessBoard( * @param piece the piece the square shall be updated to * @return a ChessBoard with updated squares. */ - def updated(square: Square, piece: Piece): ChessBoard = + def updated(square: Sqr, piece: Piece): ChessBoard = clone(squares = squares.updated(square, piece)) /** @@ -738,7 +738,7 @@ case class ChessBoard( * @param endColor color of the piece on the end square * @return the board after the move */ - private def doMove(from: Square, to: Square, piece: Piece, startColor: Color, endColor: Color): ChessBoard = { + private def doMove(from: Sqr, to: Sqr, piece: Piece, startColor: Color, endColor: Color): ChessBoard = { val updatedStatus: GameStatus = piece match { case Pawn(color, _) if to.row == ClassicalValues.piecesStartLine(color.opposite) => PromoReq(to) @@ -773,7 +773,7 @@ case class ChessBoard( else index + 1 } - private def kingIsMate(testedKing: (Square, AnyPiece)): Boolean = { + private def kingIsMate(testedKing: (Sqr, AnyPiece)): Boolean = { lazy val kingColor = turn val kingSq = testedKing._1 lazy val alliedPieces = allPieces.filter(piece => { @@ -781,7 +781,7 @@ case class ChessBoard( }) //tests if any piece can move to a specific position (-> block or capture) - def piecesCanMoveTo(sqr: Square): Boolean = { + def piecesCanMoveTo(sqr: Sqr): Boolean = { val endPiece = apply(sqr) alliedPieces exists (sp => !isPinnedPiece(sp._1) && isLegalMove(sp._1, sqr, sp._2, endPiece)) } @@ -809,10 +809,10 @@ case class ChessBoard( } @scala.annotation.tailrec - private def isEmptyConnection(from: Square, to: Square): Boolean = { + private def isEmptyConnection(from: Sqr, to: Sqr): Boolean = { val startColIndex = from.colIndx val endColIndex = to.colIndx - val incremented: Square = NumericSquare((endColIndex - startColIndex).signum, (to._2 - from._2).signum) + from + val incremented: Sqr = NumericSquare((endColIndex - startColIndex).signum, (to._2 - from._2).signum) + from if (incremented == to) true else if (apply(incremented).isEmpty) isEmptyConnection(incremented, to) else false @@ -963,12 +963,6 @@ object ChessBoard { Left(FileOperationError.FileNotFoundError(path)) } - /** - * Saves a board as XML - */ - def saveSquares(squares: Map[Char, Column]): NodeSeq = - for (x <- 1 to 8; col = columnLetter(x)) yield {squares(col).saveData} copy (label = col.toUpper toString) - /** * Converts a column index to it's corresponding character. */ @@ -1031,9 +1025,9 @@ object ChessBoard { def pawnDir(color: AnyColor): Int = if (color == White) 1 else -1 - /** @return the [[framework.Square]] where the king of this color is placed. */ - def kingStartSquare(color: AnyColor): Square = - Square('e', piecesStartLine(color)) + /** @return the [[framework.Sqr]] where the king of this color is placed. */ + def kingStartSquare(color: AnyColor): Sqr = + Sqr('e', piecesStartLine(color)) } } diff --git a/framework/src/framework/ChessIO.scala b/framework/src/framework/ChessIO.scala index df012f6..c1a4f18 100644 --- a/framework/src/framework/ChessIO.scala +++ b/framework/src/framework/ChessIO.scala @@ -26,7 +26,7 @@ import framework.Input._ * * test receiveInput DrawOffer * test receiveInput DrawReject - * test receiveInput MoveParams(from = Square('d', 2), to = Square('d', 4)) + * test receiveInput MoveParams(from = Sqr('d', 2), to = Sqr('d', 4)) * }}} * * @see [[framework.javaInterfacing.JChessIO the java variant]] diff --git a/framework/src/framework/Column.scala b/framework/src/framework/Column.scala deleted file mode 100644 index 046b694..0000000 --- a/framework/src/framework/Column.scala +++ /dev/null @@ -1,85 +0,0 @@ -package framework - -import scala.collection.IndexedSeqLike -import scala.collection.generic.CanBuildFrom -import scala.collection.mutable.ArrayBuffer - -/** - * A column that contains 8 pieces. - * - * @see [[framework.ChessBoard]] - * @version alpha 0.1 - * @author Felix Lehner - */ -final class Column(ps: Array[Piece]) extends IndexedSeq[Piece] with IndexedSeqLike[Piece, Column] { - val length = 8 - - val pieces: Array[Piece] = if (ps.length == 8) ps else Array.fill(8)(NoPiece) - - def this(ps: Map[Int, Piece]) = - this(ps.toArray.foldRight(Array.fill[Piece](8)(NoPiece)) { - (a: (Int, Piece), b: Array[Piece]) => - if (a._1 <= 8 && a._1 >= 1) b.updated(a._1 - 1, a._2) - else b - }) - - /** @return a [[framework.Column]] that is filled with a certain piece */ - def this(piece: Piece) = - this(Array.fill(8)(piece)) - - /** @return the piece at a specific position in the array */ - def pieceAt(row: Int): Piece = pieces(row - 1) - - def apply(idx: Int): Piece = pieces(idx) - - def updated(line: Int, piece: Char, color: AnyColor): Column = updated(line, piece.toUpper match { - case 'P' => Pawn(color) - case 'R' => Rook(color) - case 'N' => Knight(color) - case 'B' => Bishop(color) - case 'K' => King(color) - case 'Q' => Queen(color) - case _ => NoPiece - }) - - /** - * Replaces a piece at a specific position in [[pieces]]. - * - * @note the line parameter gets subtracted by 1 so you can access the array - * with the classical chess notation system. - * @return an updated column - */ - def updated(line: Int, piece: Piece): Column = - new Column(pieces.updated(line - 1, piece)) - - /** - * Saves this column as xml. - */ - def saveData: IndexedSeq[scala.xml.Elem] = { - var result: IndexedSeq[scala.xml.Elem] = IndexedSeq() - pieces.indices.foreach(i => if (pieces(i).nonEmpty) result = result :+ pieces(i).toXml.copy(label = "l" + (i + 1))) - result - } - - /** - * Creates a [[String]] that can be displayed in the console - */ - override def toString: String = pieces.mkString("| ", " | ", " |") - - override def newBuilder: scala.collection.mutable.Builder[Piece, Column] = Column.newBuilder -} - -object Column { - - def apply(pieces: Piece*): Column = fromSeq(pieces) - - def fromSeq(buf: Seq[Piece]): Column = new Column(buf.toArray) - - def newBuilder: scala.collection.mutable.Builder[Piece, Column] = new ArrayBuffer mapResult fromSeq - - implicit def canBuildFrom: CanBuildFrom[Column, Piece, Column] = new CanBuildFrom[Column, Piece, Column] { - def apply(): scala.collection.mutable.Builder[Piece, Column] = newBuilder - - def apply(from: Column): scala.collection.mutable.Builder[Piece, Column] = newBuilder - } -} diff --git a/framework/src/framework/IOEvents/IOEvent.scala b/framework/src/framework/IOEvents/IOEvent.scala index e224e33..b1e219d 100644 --- a/framework/src/framework/IOEvents/IOEvent.scala +++ b/framework/src/framework/IOEvents/IOEvent.scala @@ -1,7 +1,7 @@ package framework.IOEvents import framework.BoardStatus.GameResult.GameResult -import framework.Square +import framework.Sqr /** * An event given by [[framework.ChessBoard]] as reaction to specific inputs. @@ -15,7 +15,7 @@ case object ShowDrawOffer extends IOEvent case object RemoveDrawOffer extends IOEvent -final case class ShowPromotion(on: Square) extends IOEvent +final case class ShowPromotion(on: Sqr) extends IOEvent case object RemovePromotion extends IOEvent @@ -25,7 +25,7 @@ case object RemoveTakeback extends IOEvent final case class ShowEnded(result: GameResult) extends IOEvent -final case class ShowCheck(on: Square) extends IOEvent +final case class ShowCheck(on: Sqr) extends IOEvent /** Placeholder event. */ case object NoEvent extends IOEvent diff --git a/framework/src/framework/Input/Input.scala b/framework/src/framework/Input/Input.scala index 0bc90ab..5a9b06a 100644 --- a/framework/src/framework/Input/Input.scala +++ b/framework/src/framework/Input/Input.scala @@ -1,6 +1,6 @@ package framework.Input -import framework.{AnyColor, AnyPiece, Square} +import framework.{AnyColor, AnyPiece, Sqr} /** * Inputs for [[framework.ChessBoard#receive framework.ChessBoard]] or [[framework.ChessIO#receiveInput framework.ChessIO]] @@ -11,7 +11,7 @@ import framework.{AnyColor, AnyPiece, Square} sealed abstract class Input[InputType](val value: InputType) /** A move action. */ -final case class MoveParams(from: Square, to: Square) extends Input(from -> to) +final case class MoveParams(from: Sqr, to: Sqr) extends Input(from -> to) /** A promotion to a piece type. */ final case class Promotion(piece: (AnyColor, Boolean) => AnyPiece) extends Input(piece) diff --git a/framework/src/framework/MoveData.scala b/framework/src/framework/MoveData.scala index a74cabb..765f58d 100644 --- a/framework/src/framework/MoveData.scala +++ b/framework/src/framework/MoveData.scala @@ -6,21 +6,21 @@ package framework * @author Felix Lehner * @version alpha 0.2 */ -sealed class MoveData(val startPos: Square, val piece: Piece, val endPos: Square, val captured: Boolean) { +sealed class MoveData(val startPos: Sqr, val piece: Piece, val endPos: Sqr, val captured: Boolean) { def xml: scala.xml.Elem = } object MoveData { - def apply(startPos: Square, piece: Piece, endPos: Square, captured: Boolean): MoveData = + def apply(startPos: Sqr, piece: Piece, endPos: Sqr, captured: Boolean): MoveData = new MoveData(startPos, piece, endPos, captured) - def unapply(md: MoveData): Option[(Square, Piece, Square, Boolean)] = Some(md.startPos, md.piece, md.endPos, md.captured) + def unapply(md: MoveData): Option[(Sqr, Piece, Sqr, Boolean)] = Some(md.startPos, md.piece, md.endPos, md.captured) } -case class PromotionMove(override val startPos: Square, +case class PromotionMove(override val startPos: Sqr, override val piece: Piece, - override val endPos: Square, + override val endPos: Sqr, override val captured: Boolean, promoPiece: Piece) extends MoveData(startPos, piece, endPos, captured) { override def xml: scala.xml.Elem = diff --git a/framework/src/framework/Positions.scala b/framework/src/framework/Positions.scala index bcee766..b928b69 100644 --- a/framework/src/framework/Positions.scala +++ b/framework/src/framework/Positions.scala @@ -91,7 +91,7 @@ case class Position(pos: BoardMap) extends AnyVal { col <- 1 to 8 column = ChessBoard.columnLetter(col) row <- 1 to 8 - square = Square(column, row) + square = Sqr(column, row) otherPiece = other.pos(square) piece = this.pos(square) } yield { diff --git a/framework/src/framework/SaveLoader.scala b/framework/src/framework/SaveLoader.scala index 17b18be..7b92f63 100644 --- a/framework/src/framework/SaveLoader.scala +++ b/framework/src/framework/SaveLoader.scala @@ -90,9 +90,9 @@ object SaveLoader { try Right( moves map (move => MoveData( - Square(extractWithFilter(move, "start").head, extractWithFilter(move, "start").last.asDigit), + Sqr(extractWithFilter(move, "start").head, extractWithFilter(move, "start").last.asDigit), Piece(extractWithFilter(move, "movedPiece").head, Color(extractWithFilter(move, "movedPiece").last), moved = true), - Square(extractWithFilter(move, "end").head, extractWithFilter(move, "end").last.asDigit), + Sqr(extractWithFilter(move, "end").head, extractWithFilter(move, "end").last.asDigit), extractWithFilter(move, "capture").toBoolean )) toList ) @@ -202,9 +202,9 @@ object SaveLoader { val end = move \@ "end" val captured = move \@ "capture" MoveData( - Square(start.head, start.last.asDigit), + Sqr(start.head, start.last.asDigit), Piece(movedPiece.head, Color(movedPiece.last), moved = true), - Square(end.head, end.last.asDigit), + Sqr(end.head, end.last.asDigit), captured.toBoolean ) }) toList @@ -336,16 +336,16 @@ object SaveLoader { if (move.attributes.exists(_.key == "promoPiece")) { val promoPieceId = (move \@ "promoPiece").head PromotionMove( - Square(start.head, start.last.asDigit), + Sqr(start.head, start.last.asDigit), Piece(pieceId, color, moved = true), - Square(end.head, end.last.asDigit), + Sqr(end.head, end.last.asDigit), captured.toBoolean, Piece(promoPieceId, color, moved = false)) } else MoveData( - Square(start.head, start.last.asDigit), + Sqr(start.head, start.last.asDigit), Piece(pieceId, color, moved = true), - Square(end.head, end.last.asDigit), + Sqr(end.head, end.last.asDigit), captured.toBoolean) }) toList ) diff --git a/framework/src/framework/Sqr.scala b/framework/src/framework/Sqr.scala new file mode 100644 index 0000000..75dcb19 --- /dev/null +++ b/framework/src/framework/Sqr.scala @@ -0,0 +1,84 @@ +package framework + +import scala.language.implicitConversions + +/* +* TODO +* Reduce complexity to only one class, 'Sqr' or 'Sqr' +* and generate interchangeability of numerical and classic +* coordinates by method overloading. +* This will make the whole code simpler and the planned +* changes in BoardMap easier to implement. +*/ + +/** + * A coordinate of a square. + * + * @version alpha 0.1 + * @author Felix Lehner + */ +private case class Sqr(column: Int, row: Int) { + @inline + def _1: Int = column + + @inline + def _2: Int = row + + def to(square: Sqr): List[Sqr] = { + val inc = (square.column - column).signum -> (square.row - row).signum + val incremented: Sqr = this + inc + this :: (if (incremented == this) Nil else incremented to square) + } + + def until(square: Sqr): List[Sqr] = { + val inc = (square.column - column).signum -> (square.row - row).signum + val incremented = this + inc + this :: (if (incremented == square) Nil else incremented until square) + } + + def +(sq: Sqr): Sqr = Sqr(column + sq.column, row + sq.row) + + def -(sq: Sqr): Sqr = Sqr(column - sq.column, row - sq.row) + + def -(sq: (Int, Int)): Sqr = Sqr(column - sq._1, row - sq._2) + + /** + * All adjacent squares that are on the board. + */ + def validAdjacents: IndexedSeq[Sqr] = adjacents filter (_.isValid) + + /** + * All adjacent squares. + * + * @see [[framework.Sqr#validAdjacents]] for + * adjacent squares that are on the board. + */ + def adjacents: IndexedSeq[Sqr] = IndexedSeq( + (-1, -1), (-1, 0), (-1, 1), + (0, -1), (0, 1), + (1, -1), (1, 0), (1, 1) + ) map (this + _) + + def +(sq: (Int, Int)): Sqr = Sqr(column + sq._1, row + sq._2) + + /** + * Checks if this square is inside of a 8x8 board. + */ + def isValid: Boolean = column >= 1 && column <= 8 && row >= 1 && row <= 8 + + def toTuple: (Int, Int) = (column, row) + + override def toString: String = s"$column$row" +} + +object Sqr { + def apply(coordinate: String): Option[Sqr] = + if (coordinate.length == 2 && coordinate.head.isLetter && coordinate.last.isDigit) { + val col = coordinate.head + val row = coordinate.last.asDigit + Some(Sqr(col, row)) + } + else None + + def apply(column: Char, row: Int): Sqr = Sqr(ChessBoard.columnIndex(column), row) +} diff --git a/framework/src/framework/Square.scala b/framework/src/framework/Square.scala deleted file mode 100644 index e060471..0000000 --- a/framework/src/framework/Square.scala +++ /dev/null @@ -1,147 +0,0 @@ -package framework - -import scala.language.implicitConversions - -import ChessBoard.{columnLetter, columnIndex} - -/** - * A coordinate of a square. - * @version alpha 0.1 - * @author Felix Lehner - */ -sealed abstract class AbstractSqrCoordinate[ColumnType] { - val column: ColumnType - val row: Int - - type Return >: this.type <: AbstractSqrCoordinate[ColumnType] - - @inline - def _1: ColumnType = column - - @inline - def _2: Int = row - - def colIndx: Int - - def to(square: AbstractSqrCoordinate[ColumnType]): List[Return] = { - val inc = (square.colIndx - colIndx).signum -> (square.row - row).signum - val incremented: Return = this + inc - val head: Return = this - val tail: List[Return] = - if (incremented == this) Nil - else incremented.to(square).asInstanceOf[List[Return]] - head :: tail - } - - def until(square: AbstractSqrCoordinate[ColumnType]): List[Return] = { - val inc = (square.colIndx - colIndx).signum -> (square.row - row).signum - val incremented = this + inc - val head: Return = this - val tail: List[Return] = - if (incremented == square) Nil - else incremented.until(square).asInstanceOf[List[Return]] - head :: tail - } - - def +(sq: AbstractSqrCoordinate[_]): Return - - def -(sq: AbstractSqrCoordinate[_]): Return - - def +(sq: (Int, Int)): Return - - def -(sq: (Int, Int)): Return - - def unary_+ : Square = NumericSquare(colIndx, row) - - def unary_- : Square = NumericSquare(-colIndx, -row) - - /** - * All adjacent squares. - * - * @see [[framework.AbstractSqrCoordinate#validAdjacents]] for - * adjacent squares that are on the board. - */ - def adjacents: IndexedSeq[Return] = IndexedSeq( - (-1, -1), (-1, 0), (-1, 1), - (0, -1), (0, 1), - (1, -1), (1, 0), (1, 1) - ) map (this + _) - - /** - * All adjacent squares that are on the board. - */ - def validAdjacents: IndexedSeq[Return] = adjacents filter (_.isValid) - - def toTuple: (ColumnType, Int) = (column, row) - - /** - * Checks if this square is inside of a 8x8 board. - */ - def isValid: Boolean = colIndx >= 1 && colIndx <= 8 && row >= 1 && row <= 8 - - override def toString: String = s"$column$row" -} - - -final case class Square(column: Char, row: Int) extends AbstractSqrCoordinate[Char] { - val colIndx: Int = columnIndex(column) - - override type Return = Square - - override def +(sq: AbstractSqrCoordinate[_]): Return = sq match { - case Square(col, line) => - Square(columnLetter(columnIndex(column)+columnIndex(col)), row + line) - case NumericSquare(col, line) => Square(columnLetter(columnIndex(column) + col), row + line) - } - - override def -(sq: AbstractSqrCoordinate[_]): Square = this.+(-sq) - - override def +(sq: (Int, Int)): Square = Square(columnLetter(columnIndex(column) + sq._1), row + sq._2) - - override def -(sq: (Int, Int)): Square = this.+(-sq._1, -sq._2) -} - -object Square { - def apply(coordinate: String): Option[Square] = - if (coordinate.length == 2 && coordinate.head.isLetter && coordinate.last.isDigit) { - val col = coordinate.head - val row = coordinate.last.asDigit - Some(Square(col, row)) - } - else None -} - - -final case class NumericSquare(column: Int, row: Int) extends AbstractSqrCoordinate[Int] { - def colIndx: Int = column - - override type Return = NumericSquare - - override def +(sq: AbstractSqrCoordinate[_]): NumericSquare = sq match { - case Square(col, line) => NumericSquare(column + columnIndex(col), row + line) - case NumericSquare(col, line) => NumericSquare(column + col, row + line) - } - - override def -(sq: AbstractSqrCoordinate[_]): NumericSquare = this.+(-sq) - - override def +(sq: (Int, Int)): NumericSquare = NumericSquare(column + sq._1, row + sq._2) - - override def -(sq: (Int, Int)): NumericSquare = this.+(-sq._1, -sq._2) -} - -object NumericSquare { - def apply(tup: (Int, Int)) = new NumericSquare(tup._1, tup._2) -} - - -object AbstractSqrCoordinate { - /** - * implicit conversion from [[framework.Square]] to [[framework.NumericSquare]] - */ - implicit def sqr2indxSqr(sqr: Square): NumericSquare = NumericSquare(sqr.colIndx, sqr.row) - - /** - * implicit conversion from [[framework.NumericSquare]] to [[framework.Square]] - */ - implicit def indxSqr2sqr(iSqr: NumericSquare): Square = Square(columnLetter(iSqr.column), iSqr.row) -} diff --git a/framework/src/framework/javaInterfacing/JInput.java b/framework/src/framework/javaInterfacing/JInput.java index 04b7328..79c9d53 100644 --- a/framework/src/framework/javaInterfacing/JInput.java +++ b/framework/src/framework/javaInterfacing/JInput.java @@ -1,7 +1,7 @@ package framework.javaInterfacing; import framework.Input.*; -import framework.Square; +import framework.Sqr; /** * Contains wrapper for inputs for more intuitive use with java. @@ -9,7 +9,7 @@ * @version alpha 0.1 */ public class JInput { - public static MoveParams MoveParams(Square from, Square to) { + public static MoveParams MoveParams(Sqr from, Sqr to) { return new MoveParams(from, to); } diff --git a/framework/src/framework/pathfinding/BooleanPathfinder.scala b/framework/src/framework/pathfinding/BooleanPathfinder.scala index 9a4effc..a83dfe4 100644 --- a/framework/src/framework/pathfinding/BooleanPathfinder.scala +++ b/framework/src/framework/pathfinding/BooleanPathfinder.scala @@ -1,9 +1,9 @@ package framework.pathfinding -import framework.Square +import framework.Sqr /** A pathfinder that returns [[scala.Boolean]] results. */ trait BooleanPathfinder extends Pathfinder[Boolean] { - override def terminate(on: Square): Failure.type = Failure + override def terminate(on: Sqr): Failure.type = Failure - override def success(on: Square): Success[Boolean] = Success(true) + override def success(on: Sqr): Success[Boolean] = Success(true) } diff --git a/framework/src/framework/pathfinding/KingMovementPathfinder.scala b/framework/src/framework/pathfinding/KingMovementPathfinder.scala index 2ab8fdc..18dc64b 100644 --- a/framework/src/framework/pathfinding/KingMovementPathfinder.scala +++ b/framework/src/framework/pathfinding/KingMovementPathfinder.scala @@ -1,6 +1,6 @@ package framework.pathfinding -import framework.Square +import framework.Sqr /** * Applies the movement of a king to a pathfinder. @@ -8,11 +8,11 @@ import framework.Square * @version alpha 0.2 */ abstract class KingMovementPathfinder extends Pathfinder[Boolean] { - override def success(on: Square): Success[Boolean] = Success(true) + override def success(on: Sqr): Success[Boolean] = Success(true) - override def continue(from: Square): Result[Boolean] = apply(from) + override def continue(from: Sqr): Result[Boolean] = apply(from) - override def apply(pos: Square): Result[Boolean] = { + override def apply(pos: Sqr): Result[Boolean] = { val agents = Array( KingMovement(0, 1), KingMovement(0, -1), KingMovement(1, 1), KingMovement(1, 0), KingMovement(1, -1), @@ -30,12 +30,12 @@ abstract class KingMovementPathfinder extends Pathfinder[Boolean] { } } - override def terminate(on: Square): Failure.type = Failure + override def terminate(on: Sqr): Failure.type = Failure - override def decision(pos: Square): WaypointResult.Value + override def decision(pos: Sqr): WaypointResult.Value private case class KingMovement(override val vector: (Int, Int)) extends TripleDirectionalPathfinder[Boolean](vector) with BooleanPathfinder { - override def decision(pos: Square): WaypointResult.Value = KingMovementPathfinder.this.decision(pos) + override def decision(pos: Sqr): WaypointResult.Value = KingMovementPathfinder.this.decision(pos) } } diff --git a/framework/src/framework/pathfinding/MonoDirectionalPathfinder.scala b/framework/src/framework/pathfinding/MonoDirectionalPathfinder.scala index 30fa29e..468b81c 100644 --- a/framework/src/framework/pathfinding/MonoDirectionalPathfinder.scala +++ b/framework/src/framework/pathfinding/MonoDirectionalPathfinder.scala @@ -1,8 +1,8 @@ package framework.pathfinding -import framework.Square +import framework.Sqr abstract class MonoDirectionalPathfinder[@specialized(Boolean) ResultType](override val vector: (Int, Int)) extends VectorPathfinder[ResultType](vector) { - override def continue(from: Square): Result[ResultType] = this.apply(from + vector) + override def continue(from: Sqr): Result[ResultType] = this.apply(from + vector) } diff --git a/framework/src/framework/pathfinding/Pathfinder.scala b/framework/src/framework/pathfinding/Pathfinder.scala index 832fc34..73fabf7 100644 --- a/framework/src/framework/pathfinding/Pathfinder.scala +++ b/framework/src/framework/pathfinding/Pathfinder.scala @@ -1,6 +1,6 @@ package framework.pathfinding -import framework.Square +import framework.Sqr import WaypointResult._ /** @@ -11,21 +11,21 @@ import WaypointResult._ */ abstract class Pathfinder[@specialized(Boolean) ResultType] { /** ''Sets'' this pathfinder on a square to start it from there. */ - def apply(pos: Square): Result[ResultType] = decision(pos) match { + def apply(pos: Sqr): Result[ResultType] = decision(pos) match { case Termination => terminate(pos) case Positive => success(pos) case Continuation => continue(pos) } /** Terminates the pathfinder */ - def terminate(on: Square): Result[ResultType] + def terminate(on: Sqr): Result[ResultType] /** Terminates with a positive result. */ - def success(on: Square): Result[ResultType] + def success(on: Sqr): Result[ResultType] /** This lets the pathfinder continue its way. */ - def continue(from: Square): Result[ResultType] + def continue(from: Sqr): Result[ResultType] /** Decides what the pathfinder should do. */ - def decision(pos: Square): WaypointResult.Value + def decision(pos: Sqr): WaypointResult.Value } diff --git a/framework/src/framework/pathfinding/TerminableOnce.scala b/framework/src/framework/pathfinding/TerminableOnce.scala index 325c164..de79a9c 100644 --- a/framework/src/framework/pathfinding/TerminableOnce.scala +++ b/framework/src/framework/pathfinding/TerminableOnce.scala @@ -1,5 +1,5 @@ package framework.pathfinding -import framework.Square +import framework.Sqr /** * Wraps another pathfinder and lets it run in a sandbox-like @@ -11,14 +11,14 @@ case class TerminableOnce[@specialized(Boolean) ResultType](pathfinder: VectorPa extends Pathfinder[ResultType] { val pF = new TripleDirectionalPathfinder[ResultType](pathfinder.vector) { - override def success(on: Square): Result[ResultType] = pathfinder.success(on) + override def success(on: Sqr): Result[ResultType] = pathfinder.success(on) - override def decision(pos: Square): WaypointResult.Value = pathfinder.decision(pos) + override def decision(pos: Sqr): WaypointResult.Value = pathfinder.decision(pos) - override def terminate(on: Square): Result[ResultType] = TerminableOnce.this.terminate(on) + override def terminate(on: Sqr): Result[ResultType] = TerminableOnce.this.terminate(on) } - override def terminate(on: Square): Result[ResultType] = { + override def terminate(on: Sqr): Result[ResultType] = { val vectors = Array( (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), @@ -39,16 +39,16 @@ case class TerminableOnce[@specialized(Boolean) ResultType](pathfinder: VectorPa private case class TerminatedPathfinder(override val vector: (Int, Int)) extends TripleDirectionalPathfinder[ResultType](vector) { - override def terminate(on: Square): Result[ResultType] = pathfinder.terminate(on) + override def terminate(on: Sqr): Result[ResultType] = pathfinder.terminate(on) - override def success(on: Square): Result[ResultType] = pathfinder.success(on) + override def success(on: Sqr): Result[ResultType] = pathfinder.success(on) - override def decision(pos: Square): WaypointResult.Value = pathfinder.decision(pos) + override def decision(pos: Sqr): WaypointResult.Value = pathfinder.decision(pos) } - override def success(on: Square): Result[ResultType] = pathfinder.success(on) + override def success(on: Sqr): Result[ResultType] = pathfinder.success(on) - override def continue(from: Square): Result[ResultType] = pF.continue(from) + override def continue(from: Sqr): Result[ResultType] = pF.continue(from) - override def decision(pos: Square): WaypointResult.Value = pathfinder.decision(pos) + override def decision(pos: Sqr): WaypointResult.Value = pathfinder.decision(pos) } diff --git a/framework/src/framework/pathfinding/TripleDirectionalPathfinder.scala b/framework/src/framework/pathfinding/TripleDirectionalPathfinder.scala index f4bc46e..22da4e8 100644 --- a/framework/src/framework/pathfinding/TripleDirectionalPathfinder.scala +++ b/framework/src/framework/pathfinding/TripleDirectionalPathfinder.scala @@ -1,11 +1,11 @@ package framework.pathfinding -import framework.Square +import framework.Sqr abstract class TripleDirectionalPathfinder[@specialized(Boolean) ResultType](override val vector: (Int, Int)) extends VectorPathfinder[ResultType](vector) { - override def continue(from: Square): Result[ResultType] = { + override def continue(from: Sqr): Result[ResultType] = { val resMain = this.apply(from + vector) val sideVectors: Array[(Int, Int)] = @@ -15,11 +15,11 @@ abstract class TripleDirectionalPathfinder[@specialized(Boolean) ResultType](ove val resSide = sideVectors map { v => new MonoDirectionalPathfinder[ResultType](v) { - override def terminate(on: Square): Result[ResultType] = TripleDirectionalPathfinder.this.terminate(on) + override def terminate(on: Sqr): Result[ResultType] = TripleDirectionalPathfinder.this.terminate(on) - override def success(on: Square): Result[ResultType] = TripleDirectionalPathfinder.this.terminate(on) + override def success(on: Sqr): Result[ResultType] = TripleDirectionalPathfinder.this.terminate(on) - override def decision(pos: Square): WaypointResult.Value = TripleDirectionalPathfinder.this.decision(pos) + override def decision(pos: Sqr): WaypointResult.Value = TripleDirectionalPathfinder.this.decision(pos) } apply (from + v) } diff --git a/graphics/src/graphics/Board.scala b/graphics/src/graphics/Board.scala index 4de64c1..fa7fefe 100644 --- a/graphics/src/graphics/Board.scala +++ b/graphics/src/graphics/Board.scala @@ -3,7 +3,7 @@ package graphics import framework.BoardStatus.GameResult._ import framework.BoardStatus.ResultReason.DrawResultReason.InsufficientMaterial import framework.IOEvents._ -import framework.{ChessBoard, ChessIO, Square, AnyColor} +import framework.{ChessBoard, ChessIO, Sqr, AnyColor} import framework.FileOperationError._ import framework.Input._ @@ -29,7 +29,7 @@ class Board(val window: CWindow) extends GridPanel(0, 9) with BoardEventHandler else if (j == 0) new CTextField(row.toString) else { val color: BoardColor = if (i % 2 == j % 2) Brown.White else Brown.Black - val pos = Square(col, row) + val pos = Sqr(col, row) new SquareButton(color, pos, chessBoard(pos)) } } diff --git a/graphics/src/graphics/BoardEventHandler.scala b/graphics/src/graphics/BoardEventHandler.scala index 5f3f75b..34824d4 100644 --- a/graphics/src/graphics/BoardEventHandler.scala +++ b/graphics/src/graphics/BoardEventHandler.scala @@ -1,7 +1,7 @@ package graphics import framework.Input.{MoveParams, Promotion} -import framework.{AnyColor, AnyPiece, Square} +import framework.{AnyColor, AnyPiece, Sqr} /** * [Description] diff --git a/graphics/src/graphics/SquareButton.scala b/graphics/src/graphics/SquareButton.scala index 3282cb6..b2c1cd1 100644 --- a/graphics/src/graphics/SquareButton.scala +++ b/graphics/src/graphics/SquareButton.scala @@ -1,7 +1,7 @@ package graphics import BoardColors._ -import framework.Square +import framework.Sqr import scala.swing.event.ActionEvent import scala.swing.{Graphics2D, Image, Insets, ToggleButton} From 15b8541453c7b57b474413586b1c5a2ec6ce37d1 Mon Sep 17 00:00:00 2001 From: FelixL <50499590+SlaynAndKorpil@users.noreply.github.com> Date: Sun, 3 May 2020 21:55:54 +0200 Subject: [PATCH 2/4] pretty much fixed the most stuff [Insert informative description here] --- framework/src/framework/BoardMap.scala | 40 ++++--- framework/src/framework/ChessBoard.scala | 129 ++++++----------------- framework/src/framework/SaveLoader.scala | 77 +++++++------- framework/src/framework/Sqr.scala | 4 +- 4 files changed, 88 insertions(+), 162 deletions(-) diff --git a/framework/src/framework/BoardMap.scala b/framework/src/framework/BoardMap.scala index e719fc8..45c7a67 100644 --- a/framework/src/framework/BoardMap.scala +++ b/framework/src/framework/BoardMap.scala @@ -13,21 +13,23 @@ import scala.xml._ * @author Felix Lehner * @version */ -final case class BoardMap(ps: Array[Array[Piece]]) +final case class BoardMap(ps: Array[IndexedSeq[Piece]]) extends IndexedSeq[IndexedSeq[Piece]] with IndexedSeqLike[IndexedSeq[Piece], BoardMap] { override val length = 8 override def size = 8 - val pieces: Array[Array[Piece]] = if (ps.length == 8 && ps.forall(_.length == 8)) ps else Array.fill(8)(Array.fill(8)(NoPiece)) + val pieces: Array[IndexedSeq[Piece]] = if (ps.length == 8 && ps.forall(_.length == 8)) ps else Array.fill(8)(Array.fill(8)(NoPiece)) + + def apply(idx: Int): IndexedSeq[Piece] = pieces(idx) /** * @param sqr coordinates of the wanted piece * @return the chess square at a specific position on the board, [[scala.None]] if the sqr does not exist */ def getPiece(sqr: Sqr): Option[Piece] = - if (sqr.isValid) Some(apply(sqr._1)(sqr._2)) + if (sqr.isValid) Some(apply(sqr._1 - 1)(sqr._2 - 1)) else None /** @@ -49,8 +51,8 @@ final case class BoardMap(ps: Array[Array[Piece]]) */ def updated(square: Sqr, piece: Piece): BoardMap = { if (square.isValid) { - val updated = this(square._1).updated(square._2, piece) - this.updated(square._1, updated) + val updated = this(square._1 - 1).updated(square._2 - 1, piece) + this.updated(square._1 - 1, updated) } else this } @@ -59,9 +61,7 @@ final case class BoardMap(ps: Array[Array[Piece]]) * * @param sqr the square to be emptied */ - def emptySquare(sqr: Sqr): BoardMap = - if (sqr.isValid) updated(sqr, NoPiece) - else this + def emptySquare(sqr: Sqr): BoardMap = updated(sqr, NoPiece) /** * Makes a chess move. @@ -92,17 +92,13 @@ final case class BoardMap(ps: Array[Array[Piece]]) } def toXml: NodeSeq = { - for (x <- 1 to 8; col = columnLetter(x)) yield {squares(col).saveData} copy (label = col.toUpper toString) - - // in Column: - /** - * Saves this column as xml. - */ - def saveData: IndexedSeq[scala.xml.Elem] = { - var result: IndexedSeq[scala.xml.Elem] = IndexedSeq() - pieces.indices.foreach(i => if (pieces(i).nonEmpty) result = result :+ pieces(i).toXml.copy(label = "l" + (i + 1))) - result - } + for (x <- 0 to 7; col = columnLetter(x + 1)) yield + { + var result: IndexedSeq[scala.xml.Elem] = IndexedSeq() + pieces(x).indices.foreach(i => + if (pieces(x)(i).nonEmpty) result = result :+ pieces(x)(i).toXml.copy(label = "l" + (i + 1))) + result + } copy (label = col.toUpper.toString) } /** @@ -113,16 +109,18 @@ final case class BoardMap(ps: Array[Array[Piece]]) */ override def toString: String = { val separationLine: String = " +---+---+---+---+---+---+---+---+\n" - val lines = for (x <- 1 to 8; c = ChessBoard.columnLetter(x)) yield c + " " + apply(c) + val lines = for (x <- 0 to 7; c = ChessBoard.columnLetter(x + 1)) yield c + " " + apply(x) lines mkString(" 1 2 3 4 5 6 7 8\n" + separationLine, "\n" + separationLine, "\n" + separationLine) } } object BoardMap { + def fill(piece: Piece): BoardMap = BoardMap.fromSeq(Array.fill(8)(IndexedSeq.fill(8)(piece))) + def apply(pieces: Seq[Piece]*): BoardMap = fromSeq(pieces) - def fromSeq(buf: Seq[Seq[Piece]]): BoardMap = new BoardMap(buf.toArray.map(_.toArray)) + def fromSeq(buf: Seq[Seq[Piece]]): BoardMap = new BoardMap(buf.toArray.map(_.toIndexedSeq)) def newBuilder: scala.collection.mutable.Builder[Seq[Piece], BoardMap] = new ArrayBuffer mapResult fromSeq diff --git a/framework/src/framework/ChessBoard.scala b/framework/src/framework/ChessBoard.scala index baeda03..e0bf5c8 100644 --- a/framework/src/framework/ChessBoard.scala +++ b/framework/src/framework/ChessBoard.scala @@ -55,14 +55,6 @@ case class ChessBoard( /** The move counter */ val turnCounter: Int = history.length / 2 - /** - * @param column the one-letter identifier of a column of squares on the board - * @return a column of the board, [[scala.None]] if no column with this identifier exists - */ - def getColumn(column: Char): Option[Column] = - if (isValidColumn(column)) Some(squares(column)) - else None - /** * Handles different input types depending on the [[framework.ChessBoard#gameStatus gameStatus]]. * @@ -144,10 +136,9 @@ case class ChessBoard( /** * Filters for all pieces that match a predicate. - * */ - def filterPieces(func: Piece => Boolean): Map[Char, Column] = - squares map { tup => tup._1 -> tup._2.filter(func) } + def filterPieces(func: Piece => Boolean): BoardMap = + squares map { tup => tup.filter(func) } /** * Generates a new [[framework.ChessBoard ChessBoard]] which shares all attributes with this one @@ -316,8 +307,8 @@ case class ChessBoard( * @return `true` if the move is legal, otherwise `false` */ def isLegalMove(start: Sqr, end: Sqr, startPiece: Piece, endPiece: Piece): Boolean = { - val startCIndex = start.colIndx - val endCIndex = end.colIndx + val startCIndex = start.column + val endCIndex = end.column val columnDif = endCIndex - startCIndex val lineDif = end._2 - start._2 @@ -352,13 +343,10 @@ case class ChessBoard( (!startPiece.moved && (end.column == 'c' || end.column == 'g') && { val rookCol = if (end.column == 'c') 'a' else 'h' val rook = apply(Sqr(rookCol, ClassicalValues.piecesStartLine(color))) - val squaresToTest: List[NumericSquare] = - AbstractSqrCoordinate.sqr2indxSqr(Sqr(if (startCIndex < endCIndex) 'g' else 'c', start.row)) to start + val squaresToTest: List[Sqr] = + Sqr(if (startCIndex < endCIndex) 'g' else 'c', start.row) to start - def isSqrAttacked(sqr: AbstractSqrCoordinate[_]): Boolean = sqr match { - case square: Sqr => isAttacked(square, turn, withKing = true) - case square: NumericSquare => isAttacked(square, turn, withKing = true) - } + def isSqrAttacked(sqr: Sqr): Boolean = isAttacked(sqr, turn, withKing = true) rook == Rook(color, false) && squaresToTest.forall(sqr => !isSqrAttacked(sqr)) && isEmptyOrthogonal(start, end) }) @@ -405,12 +393,12 @@ case class ChessBoard( } val opponent = attacked.opposite - val colI = sqr.colIndx + val colI = sqr.column val row = sqr.row - def partNxtPiece(inc: (Int, Int)): Piece = nextPiece(NumericSquare(colI, sqr.row), NumericSquare(inc)) + def partNxtPiece(inc: (Int, Int)): Piece = nextPiece(sqr, Sqr(inc)) - def partApply(inc: (Int, Int)) = apply(NumericSquare(colI, row) + inc) + def partApply(inc: (Int, Int)) = apply(sqr + inc) val attackedByKnight: Int = Array(partApply(1, 2), partApply(2, 1), partApply(2, -1), partApply(1, -2), partApply(-1, 2), @@ -432,7 +420,7 @@ case class ChessBoard( val attackedByPawn: Int = { val dir = ClassicalValues.pawnDir(attacked) - def pieceAtOffset(colD: Int, rowD: Int): Piece = apply(NumericSquare(colI, row) + (colD, rowD)) + def pieceAtOffset(colD: Int, rowD: Int): Piece = apply(sqr + (colD, rowD)) val arr = Array(pieceAtOffset(1, dir), pieceAtOffset(-1, dir)) @@ -462,7 +450,7 @@ case class ChessBoard( case anyPiece: AnyPiece => val kings = allPieces filter (_._2 === King(anyPiece.color)) kings exists { king => - val negVec = NumericSquare((square.colIndx - king._1.colIndx).signum, (square.row - king._1.row).signum) + val negVec = Sqr((square.column - king._1.column).signum, (square.row - king._1.row).signum) val possiblePinner = nextPiece(square, negVec) @@ -644,7 +632,7 @@ case class ChessBoard( val piecesOfColor = // pieces of specified color without kings allPieces .map { tup => /*replace square with square color*/ - (if (tup._1.colIndx % 2 == tup._1.row % 2) Black else White, tup._2) } + (if (tup._1.column % 2 == tup._1.row % 2) Black else White, tup._2) } .filter(_._2.color == color) .filterNot(_._2 === King(color)) @@ -684,7 +672,7 @@ case class ChessBoard( * @return the first piece on a line */ @scala.annotation.tailrec - final def nextPiece(start: NumericSquare, increment: NumericSquare): Piece = { + final def nextPiece(start: Sqr, increment: Sqr): Piece = { val incremented = start + increment if (incremented.isValid) apply(incremented) match { case NoPiece => nextPiece(incremented, increment) @@ -710,8 +698,8 @@ case class ChessBoard( * @see [[framework.ChessBoard#isEmptyOrthogonal isEmptyOrthogonal]] */ def isEmptyDiagonal(from: Sqr, to: Sqr): Boolean = { - val startColIndex = from.colIndx - val endColIndex = to.colIndx + val startColIndex = from.column + val endColIndex = to.column val diagonal = startColIndex - endColIndex == from._2 - to._2 || startColIndex - to._2 == endColIndex - from._2 diagonal && isEmptyConnection(from, to) } @@ -810,9 +798,9 @@ case class ChessBoard( @scala.annotation.tailrec private def isEmptyConnection(from: Sqr, to: Sqr): Boolean = { - val startColIndex = from.colIndx - val endColIndex = to.colIndx - val incremented: Sqr = NumericSquare((endColIndex - startColIndex).signum, (to._2 - from._2).signum) + from + val startColIndex = from.column + val endColIndex = to.column + val incremented: Sqr = Sqr((endColIndex - startColIndex).signum, (to._2 - from._2).signum) + from if (incremented == to) true else if (apply(incremented).isEmpty) isEmptyConnection(incremented, to) else false @@ -840,57 +828,19 @@ object ChessBoard { * @return a fully filled [[framework.ChessBoard ChessBoard]] */ def fill(piece: Piece)(implicit io: ChessIO): ChessBoard = - this(Array.fill(8)(Column(piece)), Nil, Positions.empty, White).get + this(BoardMap.fill(piece), Nil, Positions.empty, White, StandardReq) val classicalPosition: BoardMap = BoardMap( - 'a' -> new Column(Map( - 1 -> Rook(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Rook(Black) - )), - 'b' -> new Column(Map( - 1 -> Knight(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Knight(Black) - )), - 'c' -> new Column(Map( - 1 -> Bishop(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Bishop(Black) - )), - 'd' -> new Column(Map( - 1 -> Queen(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Queen(Black) - )), - 'e' -> new Column(Map( - 1 -> King(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> King(Black) - )), - 'f' -> new Column(Map( - 1 -> Bishop(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Bishop(Black) - )), - 'g' -> new Column(Map( - 1 -> Knight(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Knight(Black) - )), - 'h' -> new Column(Map( - 1 -> Rook(White), - 2 -> Pawn(White), - 7 -> Pawn(Black), - 8 -> Rook(Black) - ))) + Array(Rook(White), Knight(White), Bishop(White), Queen(White), King(White), Bishop(White), Knight(White), Rook(White)), + Array.fill(8)(Pawn(White)), + Array.fill(8)(NoPiece), + Array.fill(8)(NoPiece), + Array.fill(8)(NoPiece), + Array.fill(8)(NoPiece), + Array.fill(8)(NoPiece), + Array.fill(8)(NoPiece), + Array.fill(8)(Pawn(Black)), + Array(Rook(Black), Knight(Black), Bishop(Black), Queen(Black), King(Black), Bishop(Black), Knight(Black), Rook(Black))) /** * Defines the classical chess standard board @@ -900,25 +850,6 @@ object ChessBoard { def classicalBoard(implicit io: ChessIO): ChessBoard = ChessBoard(classicalPosition, Nil, Positions.empty, White, StandardReq)(io, ClassicPosition) - /** - * Generator for chessboards. - */ - def apply(columns: Array[Column], history: List[MoveData], positions: Positions, turn: AnyColor) - (implicit io: ChessIO): Option[ChessBoard] = - if (columns.length >= 8) { - val board = BoardMap( - 'a' -> columns(0), - 'b' -> columns(1), - 'c' -> columns(2), - 'd' -> columns(3), - 'e' -> columns(4), - 'f' -> columns(5), - 'g' -> columns(6), - 'h' -> columns(7) - ) - Some(new ChessBoard(board, history, positions, turn, StandardReq)(io, ArbitraryPosition(board))) - } else None - /** * Saves a [[framework.ChessBoard]] to a file using the xml format. * diff --git a/framework/src/framework/SaveLoader.scala b/framework/src/framework/SaveLoader.scala index 7b92f63..1a22f5d 100644 --- a/framework/src/framework/SaveLoader.scala +++ b/framework/src/framework/SaveLoader.scala @@ -4,6 +4,7 @@ import FileOperationError._ import BoardStatus.GameStatus._ import ChessBoard.columnLetter +import scala.language.postfixOps import scala.xml._ /** @@ -54,12 +55,12 @@ object SaveLoader { (xml \ nodeName).text.filter(c => c != ' ' && c != '\n') trait Loader { - def loadSquaresFromXML(xml: Node): Either[FileOperationError, Map[Char, Column]] + def loadSquaresFromXML(xml: Node): Either[FileOperationError, BoardMap] /** Loads a board from toXml */ def load(xml: Elem)(implicit io: ChessIO): Either[FileOperationError, ChessBoard] - def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, Column] + def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, IndexedSeq[Piece]] def loadPieceFromXML(xml: NodeSeq): Either[FileOperationError, Piece] } @@ -68,11 +69,11 @@ object SaveLoader { * Chosen when no other loader is defined for a specific version */ object NoLoaderDefined extends Loader { - override def loadSquaresFromXML(xml: Node): Either[FileOperationError, Map[Char, Column]] = Left(BoardLoadingError(xml.toString)) + override def loadSquaresFromXML(xml: Node): Either[FileOperationError, BoardMap] = Left(BoardLoadingError(xml.toString)) override def load(xml: Elem)(implicit io: ChessIO): Either[FileOperationError, ChessBoard] = Left(UnknownVersionError) - override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, Column] = Left(ColumnLoadingError(xml.toString)) + override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, IndexedSeq[Piece]] = Left(ColumnLoadingError(xml.toString)) override def loadPieceFromXML(xml: NodeSeq): Either[FileOperationError, Piece] = Left(PieceLoadingError(xml.toString)) } @@ -86,15 +87,14 @@ object SaveLoader { case col: AnyColor => val squares = loadSquaresFromXML(boardData.head) - val history = + val history: Either[HistoryLoadingError, List[MoveData]] = try Right( - moves map (move => - MoveData( + (moves map {move => MoveData( Sqr(extractWithFilter(move, "start").head, extractWithFilter(move, "start").last.asDigit), Piece(extractWithFilter(move, "movedPiece").head, Color(extractWithFilter(move, "movedPiece").last), moved = true), Sqr(extractWithFilter(move, "end").head, extractWithFilter(move, "end").last.asDigit), extractWithFilter(move, "capture").toBoolean - )) toList + )}).toList ) catch { case _: Throwable => Left(HistoryLoadingError(moves.toString)) @@ -129,32 +129,31 @@ object SaveLoader { } override def loadSquaresFromXML(xml: Node): Either[FileOperationError, BoardMap] = { - val loadedSquares: IndexedSeq[(Char, Either[FileOperationError, Column])] = for { + val loadedSquares: IndexedSeq[Either[FileOperationError, IndexedSeq[Piece]]] = for { x <- 1 to 8 col = columnLetter(x) - } yield col -> loadColumnFromXML(xml \ col.toString.toUpperCase) - val possibleError = loadedSquares find (_._2.isLeft) map (_._2.left) + } yield loadColumnFromXML(xml \ col.toString.toUpperCase) + val possibleError = loadedSquares find (_.isLeft) map (_.left) + possibleError match { case None => - val res = loadedSquares.map { tup => - (tup._1, tup._2.right.get) - } - Right(BoardMap(res)) + val res = loadedSquares map { piece => piece.right.get } + Right(BoardMap.fromSeq(res)) case Some(error) => Left(error.get) } } /** - * Loads a [[framework.Column]] from toXml data. + * Loads a column from toXml data. * * @param xml data formatted as toXml * @return the loaded column */ - override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, Column] = { + override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, IndexedSeq[Piece]] = { try { - var pieces: Array[Piece] = Array.empty - var errors: Array[FileOperationError] = Array.empty + var pieces: IndexedSeq[Piece] = IndexedSeq.empty + var errors: IndexedSeq[FileOperationError] = IndexedSeq.empty val loadedData = for { i <- 1 to 8 @@ -167,7 +166,7 @@ object SaveLoader { case Left(error) => errors :+= error } - if (pieces.length >= 8) Right(new Column(pieces)) + if (pieces.length >= 8) Right(pieces) else Left(errors.head) } catch { @@ -248,29 +247,27 @@ object SaveLoader { } override def loadSquaresFromXML(xml: Node): Either[FileOperationError, BoardMap] = { - val loadedSquares: IndexedSeq[(Char, Either[FileOperationError, Column])] = for { + val loadedSquares: IndexedSeq[Either[FileOperationError, IndexedSeq[Piece]]] = for { x <- 1 to 8 col = columnLetter(x) - } yield col -> loadColumnFromXML(xml \ col.toString.toUpperCase) - val possibleError = loadedSquares find (_._2.isLeft) map (_._2.left) + } yield loadColumnFromXML(xml \ col.toString.toUpperCase) + val possibleError = loadedSquares find (_.isLeft) map (_.left) possibleError match { case None => - val res = loadedSquares.map { tup => - (tup._1, tup._2.right.get) - } - Right(BoardMap(res)) + val res = loadedSquares.map { _.right.get } + Right(BoardMap.fromSeq(res)) case Some(error) => Left(error.get) } } /** - * Loads a [[framework.Column]] from toXml data. + * Loads a column from toXml data. * * @param xml data formatted as toXml * @return the loaded column */ - override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, Column] = { + override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, IndexedSeq[Piece]] = { try { var pieces: Array[Piece] = Array.empty var errors: Array[FileOperationError] = Array.empty @@ -286,7 +283,7 @@ object SaveLoader { case Left(error) => errors :+= error } - if (pieces.length >= 8) Right(new Column(pieces)) + if (pieces.length >= 8) Right(pieces) else Left(errors.head) } catch { @@ -369,7 +366,7 @@ object SaveLoader { if (moveHistory.isEmpty) Positions.empty else { val boards: IndexedSeq[BoardMap] = moveHistory.tail.foldRight(Array(startPos.right.get.squares)) { - (move, positions) => getNextPositionAfterMove(BoardMap(positions.head), move) +: positions + (move, positions) => getNextPositionAfterMove(BoardMap.fromSeq(positions.head), move) +: positions } Positions(boards map Position) } @@ -405,29 +402,27 @@ object SaveLoader { } override def loadSquaresFromXML(xml: Node): Either[FileOperationError, BoardMap] = { - val loadedSquares: IndexedSeq[(Char, Either[FileOperationError, Column])] = for { + val loadedSquares: IndexedSeq[Either[FileOperationError, IndexedSeq[Piece]]] = for { x <- 1 to 8 col = columnLetter(x) - } yield col -> loadColumnFromXML(xml \ col.toString.toUpperCase) - val possibleError = loadedSquares find (_._2.isLeft) map (_._2.left) + } yield loadColumnFromXML(xml \ col.toString.toUpperCase) + val possibleError = loadedSquares find (_.isLeft) map (_.left) possibleError match { case None => - val res = loadedSquares.map { tup => - (tup._1, tup._2.right.get) - } - Right(BoardMap(res)) + val res = loadedSquares.map { _.right.get } + Right(BoardMap.fromSeq(res)) case Some(error) => Left(error.get) } } /** - * Loads a [[framework.Column]] from toXml data. + * Loads a column from toXml data. * * @param xml data formatted as toXml * @return the loaded column */ - override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, Column] = { + override def loadColumnFromXML(xml: NodeSeq): Either[FileOperationError, IndexedSeq[Piece]] = { try { var pieces: Array[Piece] = Array.empty var errors: Array[FileOperationError] = Array.empty @@ -443,7 +438,7 @@ object SaveLoader { case Left(error) => errors :+= error } - if (pieces.length >= 8) Right(new Column(pieces)) + if (pieces.length >= 8) Right(pieces) else Left(errors.head) } catch { diff --git a/framework/src/framework/Sqr.scala b/framework/src/framework/Sqr.scala index 75dcb19..f88b622 100644 --- a/framework/src/framework/Sqr.scala +++ b/framework/src/framework/Sqr.scala @@ -17,7 +17,7 @@ import scala.language.implicitConversions * @version alpha 0.1 * @author Felix Lehner */ -private case class Sqr(column: Int, row: Int) { +case class Sqr(column: Int, row: Int) { @inline def _1: Int = column @@ -81,4 +81,6 @@ object Sqr { else None def apply(column: Char, row: Int): Sqr = Sqr(ChessBoard.columnIndex(column), row) + + def apply(sqr: (Int, Int)): Sqr = Sqr(sqr._1, sqr._2) } From 4643278b38668d1475e8dcbd57bcddab7fee79f4 Mon Sep 17 00:00:00 2001 From: FelixL <50499590+SlaynAndKorpil@users.noreply.github.com> Date: Tue, 12 May 2020 20:11:41 +0200 Subject: [PATCH 3/4] got it to compile! but it's still broken also simplified BoardMap to just hold a 2-dimensional Array --- .idea/runConfigurations/Main.xml | 1 - framework/src/framework/BoardMap.scala | 65 +++++++++---------- framework/src/framework/ChessBoard.scala | 24 ++++--- framework/src/framework/SaveLoader.scala | 34 +++++----- framework/src/framework/Sqr.scala | 19 ------ framework/src/framework/StartPosition.scala | 2 +- graphics/src/graphics/Board.scala | 10 +-- graphics/src/graphics/BoardEventHandler.scala | 4 +- graphics/src/graphics/SquareButton.scala | 2 +- 9 files changed, 67 insertions(+), 94 deletions(-) diff --git a/.idea/runConfigurations/Main.xml b/.idea/runConfigurations/Main.xml index 466c308..caeb46e 100644 --- a/.idea/runConfigurations/Main.xml +++ b/.idea/runConfigurations/Main.xml @@ -2,7 +2,6 @@