Browse Source

Piece movement rules

Roman Hergenreder 5 years ago
parent
commit
00823344c1
24 changed files with 642 additions and 60 deletions
  1. 43 0
      .gitignore
  2. 6 18
      Makefile
  3. 146 10
      src/Field.cpp
  4. 12 17
      src/Field.h
  5. BIN
      src/Field.h.gch
  6. 102 0
      src/Game.cpp
  7. 29 0
      src/Game.h
  8. 1 0
      src/Piece.cpp
  9. 23 11
      src/Piece.h
  10. BIN
      src/Piece.h.gch
  11. 28 0
      src/base.h
  12. 22 0
      src/pieces/Bishop.cpp
  13. 16 0
      src/pieces/Bishop.h
  14. 52 0
      src/pieces/King.cpp
  15. 21 0
      src/pieces/King.h
  16. 14 0
      src/pieces/Knight.cpp
  17. 16 0
      src/pieces/Knight.h
  18. 41 2
      src/pieces/Pawn.cpp
  19. 2 2
      src/pieces/Pawn.h
  20. BIN
      src/pieces/Pawn.h.gch
  21. 17 0
      src/pieces/Queen.cpp
  22. 16 0
      src/pieces/Queen.h
  23. 19 0
      src/pieces/Rook.cpp
  24. 16 0
      src/pieces/Rook.h

+ 43 - 0
.gitignore

@@ -0,0 +1,43 @@
+
+# Created by https://www.gitignore.io/api/c++
+# Edit at https://www.gitignore.io/?templates=c++
+
+### C++ ###
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# End of https://www.gitignore.io/api/c++
+
+
+# Binary
+chess

+ 6 - 18
Makefile

@@ -8,30 +8,18 @@ MKDIR_P := mkdir -p
 SRC_FILES := $(shell find $(SRC_DIR) -name '*.cpp')
 OBJ_FILES := $(addprefix $(OBJ_DIR)/,$(SRC_FILES:$(SRC_DIR)/%.cpp=%.o))
 
+all: dir chess
+
+dir:
+	$(MKDIR_P) $(OBJ_DIR)
+
 chess: $(OBJ_FILES)
 	$(CXX) $(LDFLAGS) -o $@ $^
 
 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
+	$(MKDIR_P) $(shell dirname $@)
 	$(CXX) $(CFLAGS) -c -o $@ $<
 
-# test:
-# 	echo $(SRC_FILES)
-#
-# all: compile link
-#
-# compile: build $(SRC_FILES)
-#
-# build: $(SRC_FILES)
-# 	echo $@
-# 	$(CXX) $(CFLAGS) $(INCLUDE) -o $@ $<
-#
-# $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
-# 	${MKDIR_P} $(dir $@)
-# 	$(CXX) $(CFLAGS) -o $@ $(OBJ_FILES)
-
-link:
-	${CXX} -o chess ${CFLAGS} ${OBJ_FILES} ${LDFLAGS} ${LDLIBS}
-
 clean:
 	rm -rf ./obj/*.o
 	rm -rf ./obj/**/*.o

+ 146 - 10
src/Field.cpp

@@ -1,24 +1,160 @@
 #include "Field.h"
-
 #include "pieces/Pawn.h"
+#include "pieces/King.h"
+#include "pieces/Queen.h"
+#include "pieces/Rook.h"
+#include "pieces/Bishop.h"
+#include "pieces/Knight.h"
 
 CField::CField() {
   reset();
 }
 
 void CField::reset() {
-  for(unsigned int x = 0; x < 8; x++) {
-    for(unsigned int y = 0; y < 8; y++) {
-      if(y == 1)
-        pieces[x][y] = new CPawn(CPiece::Color::WHITE);
-      else if(y == 2)
-        pieces[x][y] = new CPawn(CPiece::Color::BLACK);
+
+  // White
+  pieces[0][0] = new CRook(Color::WHITE);
+  pieces[7][0] = new CRook(Color::WHITE);
+
+  pieces[1][0] = new CKnight(Color::WHITE);
+  pieces[6][0] = new CKnight(Color::WHITE);
+
+  pieces[2][0] = new CBishop(Color::WHITE);
+  pieces[5][0] = new CBishop(Color::WHITE);
+
+  pieces[3][0] = new CQueen(Color::WHITE);
+  pieces[4][0] = (m_pKingWhite = new CKing(Color::WHITE));
+  for(unsigned int x = 0; x < 8; x++)
+    pieces[x][1] = new CPawn(Color::WHITE);
+
+  // Empty Space
+  for(unsigned x = 0; x < 8; x++)
+    for(unsigned y = 2; y < 6; y++)
+      pieces[x][y] = nullptr;
+
+  // Black
+  pieces[0][7] = new CRook(Color::BLACK);
+  pieces[7][7] = new CRook(Color::BLACK);
+
+  pieces[1][7] = new CKnight(Color::BLACK);
+  pieces[6][7] = new CKnight(Color::BLACK);
+
+  pieces[2][7] = new CBishop(Color::BLACK);
+  pieces[5][7] = new CBishop(Color::BLACK);
+
+  pieces[3][7] = new CQueen(Color::BLACK);
+  pieces[4][7] = (m_pKingBlack = new CKing(Color::BLACK));
+  for(unsigned int x = 0; x < 8; x++)
+    pieces[x][6] = new CPawn(Color::BLACK);
+}
+
+ostream& operator<<(ostream &os, const CField &field) {
+  os << "  ";
+  for(int x = 0; x < 8; x++)
+    os << "  " << (char)((int)'A' + x);
+  os << endl;
+
+  for(int y = 7; y >= 0; y--) {
+    os << " " << y + 1;
+    for(int x = 0; x < 8; x++) {
+      os << "  ";
+      if(field.pieces[x][y] != nullptr)
+        os << *field.pieces[x][y];
+      else
+        os << " ";
+    }
+    os << endl;
+  }
+
+  return os;
+}
+
+bool CField::checkBounds(int x, int y) {
+  return (x >= 0 && y >= 0 && x < 8 && y < 8);
+}
+
+bool CField::isAttacked(int x, int y, int color) {
+  if(!checkBounds(x, y))
+    return false;
+
+  // TODO: CField::isAttacked(int,int,int)
+  return false;
+}
+
+void CField::swapPieces(int x0, int y0, int x1, int y1) {
+  if(!checkBounds(x0, y0) || !checkBounds(x1, y1))
+    return;
+
+  CPiece *piece0 = getPiece(x0, y0);
+  CPiece *piece1 = getPiece(x1, y1);
+  pieces[x0][y0] = piece1;
+  pieces[x1][y1] = piece0;
+}
+
+bool CField::move(int fromX, int fromY, int toX, int toY) {
+  if(!checkBounds(fromX, fromY) || !checkBounds(fromX, fromY))
+    return false;
+
+  if(fromX == toX && fromY == toY)
+    return false;
+
+  CPiece *movingPiece = getPiece(fromX, fromY);
+  if(movingPiece == nullptr)
+    return false;
+
+  CPiece *targetPiece = getPiece(toX, toY);
+  if(targetPiece != nullptr) {
+    if(targetPiece->getType() == CPiece::Type::KING)
+      return false;
+    else if(targetPiece->getColor() == movingPiece->getColor())
+      return false;
+  }
+
+  if(movingPiece->canMove(this, fromX, fromY, toX, toY)) {
+    if(targetPiece != nullptr) {
+      delete targetPiece;
     }
+    pieces[toX][toY] = movingPiece;
+    pieces[fromX][fromY] = nullptr;
+    movingPiece->m_HasMoved = true;
+    return true;
+  } else {
+    return false;
   }
 }
 
+int CField::normalalize(int i) {
+  return (i == 0 ? 0 : i / abs(i));
+}
+
+/*
+* returns false, if no intersection occurs, true otherwise
+*/
+bool CField::intersect(int fromX, int fromY, int toX, int toY, bool includeTarget) {
+  if(!checkBounds(fromX, fromY) || !checkBounds(fromX, fromY))
+    return false;
+
+  int dirX = toX - fromX;
+  int dirY = toY - fromY;
+
+  // No move
+  if(dirX == 0 && dirY == 0)
+    return false;
+
+  // We cannot calculate intersection for this direction, e.g. dirX = 5, dirY = 3
+  if(dirX != 0 && dirY != 0 && abs(dirX) != abs(dirY))
+    return false;
+
+  dirX = normalalize(dirX);
+  dirY = normalalize(dirY);
+
+  int x = fromX + dirX;
+  int y = fromY + dirY;
+
+  for(; !(x == toX && y == toY); x += dirX, y += dirY) {
+    if(pieces[x][y] != nullptr)
+      return true;
+  }
 
-int main(int argc, char *argv[]) {
-  CField field;
-  cout << field;
+  return (includeTarget ? pieces[toX][toY] != nullptr : false);
 }

+ 12 - 17
src/Field.h

@@ -3,11 +3,12 @@
 
 #include <iostream>
 
-using namespace std;
-
 class CField {
 
   class CPiece *pieces[8][8];
+  class CKing *m_pKingWhite, *m_pKingBlack;
+
+  static inline int normalalize(int i);
 
 public:
 
@@ -16,23 +17,17 @@ public:
 
   void reset();
 
-  friend ostream& operator<<(ostream &os, const CField &piece) {
-    os << "  ";
-    for(unsigned int y = 0; y < 8; y++)
-      os << "  " << (char)((int)'A' + y);
-    os << endl;
-
-    for(unsigned int y = 0; y < 8; y++) {
-      os << " " << y;
-      for(unsigned int x = 0; x < 8; x++) {
-        os << "  " << (piece.pieces[x][y] != nullptr) ? piece.pieces[x][y]->m_Icon : " ";
-      }
-      os << endl;
-    }
+  friend std::ostream& operator<<(std::ostream &os, const CField &field);
 
-    return os;
-  }
+  bool isAttacked(int x, int y, int color);
+  void swapPieces(int x0, int y0, int x1, int y1);
+  bool move(int fromX, int fromY, int toX, int toY);
+  bool intersect(int fromX, int fromY, int toX, int toY, bool includeTarget=false);
+  bool checkBounds(int x, int y);
 
+  class CKing *getKingBlack() const { return m_pKingBlack; }
+  class CKing *getKingWhite() const { return m_pKingWhite; }
+  class CPiece *getPiece(int x, int y) { return pieces[x][y]; }
 };
 
 #endif

BIN
src/Field.h.gch


+ 102 - 0
src/Game.cpp

@@ -0,0 +1,102 @@
+#include <regex>
+
+#include "Game.h"
+#include "Field.h"
+#include "base.h"
+#include "pieces/King.h"
+
+using namespace std;
+
+CGame::CGame() {
+  m_CurrentPlayer = Color::WHITE;
+  m_pField = new CField();
+}
+
+CKing *CGame::getActiveKing() {
+  return (m_CurrentPlayer == Color::WHITE ? m_pField->getKingWhite() : m_pField->getKingBlack());
+}
+
+bool CGame::parsePosition(string pos, int *x, int *y) {
+
+  if(pos.length() != 2)
+    return false;
+
+  int first = pos[0] - (pos[0] >= (int)('a') ? (int)('a') : (int)('A'));
+  int second = pos[1] - (int)('1');
+
+  if(first < 0 || first >= 8)
+    return false;
+
+  if(second < 0 || second >= 8)
+    return false;
+
+  *x = first;
+  *y = second;
+  return true;
+}
+
+bool CGame::processInput(string line) {
+  trim(line);
+
+  if(line.compare("q") == 0 || line.compare("quit") == 0 || line.compare("exit") == 0) {
+    m_IsOver = true;
+    return true;
+  } else if(line.compare("0-0") == 0 || line.compare("O-O") == 0) {
+    CKing *activeKing = getActiveKing();
+    if(!activeKing->performKingsideCasteling(m_pField)) {
+      cout << "Your king can't castle kingside right now." << endl;
+      return false;
+    } else {
+      return true;
+    }
+  } else if(line.compare("0-0-0") == 0 || line.compare("O-O-O") == 0) {
+    CKing *activeKing = getActiveKing();
+    if(!activeKing->performQueensideCasteling(m_pField)) {
+      cout << "Your king can't castle queenside right now." << endl;
+      return false;
+    } else {
+      return true;
+    }
+  } else {
+    regex rgx("^(.*)-(.*)$");
+    smatch match;
+    if (regex_search(line, match, rgx)) {
+      int fromX, fromY, toX, toY;
+      if(parsePosition(match[1].str(), &fromX, &fromY) && parsePosition(match[2].str(), &toX, &toY)) {
+        CPiece *piece = m_pField->getPiece(fromX, fromY);
+        if(piece != nullptr && piece->getColor() == m_CurrentPlayer && m_pField->move(fromX, fromY, toX, toY)) {
+          return true;
+        }
+      }
+    }
+  }
+
+  cout << "Invalid Input!" << endl;
+  return false;
+}
+
+void CGame::start() {
+
+  m_IsOver = false;
+  m_pField->reset();
+  string line;
+
+  while(!m_IsOver) {
+    cout << endl << *m_pField << endl;
+
+    do {
+      cout << "[" << (m_CurrentPlayer == Color::WHITE ? "WHITE" : "BLACK") << " Turn]: ";
+      cin >> line;
+    } while(!processInput(line));
+
+    // check win condition
+    {
+      m_CurrentPlayer = 1 - m_CurrentPlayer;
+    }
+  }
+}
+
+int main() {
+  CGame game;
+  game.start();
+}

+ 29 - 0
src/Game.h

@@ -0,0 +1,29 @@
+#ifndef GAME_H
+#define GAME_H
+
+enum Color {
+  WHITE = 0,
+  BLACK
+};
+
+#include <string>
+
+class CGame {
+
+  int m_CurrentPlayer;
+  class CField *m_pField;
+  bool m_IsOver;
+  bool processInput(std::string line);
+  bool parsePosition(std::string pos, int *x, int *y);
+
+  class CKing *getActiveKing();
+
+public:
+
+  CGame();
+
+  void start();
+
+};
+
+#endif

+ 1 - 0
src/Piece.cpp

@@ -0,0 +1 @@
+#include "Piece.h"

+ 23 - 11
src/Piece.h

@@ -1,8 +1,8 @@
 #ifndef PIECE_H
 #define PIECE_H
 
+#include "Game.h"
 #include <string>
-#include <iostream>
 
 using namespace std;
 
@@ -10,30 +10,42 @@ class CPiece {
 
 public:
 
-  enum Color {
-    WHITE = 0,
-    BLACK
+  enum Type {
+    PAWN = 0,
+    ROOK,
+    KNIGHT,
+    BISHOP,
+    KING,
+    QUEEN
   };
 
 private:
 
-  string m_Icon;
-  int m_Color;
+  const Type m_Type;
+  const string m_Icon;
+  const int m_Color;
 
 public:
 
-  CPiece(string icon, int color) {
-    m_Icon = icon;
-    m_Color = color;
+  CPiece(Type type, string icon, int color) :
+    m_Type(type), m_Icon(icon), m_Color(color) {
+    m_HasMoved = false;
   }
 
   ~CPiece() { }
 
-  friend ostream &operator<<(ostream &os, const CPiece &piece) {
-    cout << "Printing" << endl;
+  bool m_HasMoved;
+
+  friend std::ostream& operator<<(std::ostream &os, const CPiece &piece) {
     return os << piece.m_Icon;
   }
 
+  Type getType() const { return m_Type; }
+  int getColor() const { return m_Color; }
+  bool hasMoved() const { return m_HasMoved; }
+
+  // Virtual methods
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY) = 0;
 };
 
 #endif

BIN
src/Piece.h.gch


+ 28 - 0
src/base.h

@@ -0,0 +1,28 @@
+#ifndef BASE_H
+#define BASE_H
+
+#include <algorithm>
+#include <cctype>
+#include <locale>
+
+// trim from start (in place)
+static inline void ltrim(std::string &s) {
+    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
+        return !std::isspace(ch);
+    }));
+}
+
+// trim from end (in place)
+static inline void rtrim(std::string &s) {
+    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
+        return !std::isspace(ch);
+    }).base(), s.end());
+}
+
+// trim from both ends (in place)
+static inline void trim(std::string &s) {
+    ltrim(s);
+    rtrim(s);
+}
+
+#endif

+ 22 - 0
src/pieces/Bishop.cpp

@@ -0,0 +1,22 @@
+#include "Bishop.h"
+#include "../Field.h"
+
+CBishop::CBishop(int color)
+  : CPiece(Type::BISHOP, (color == Color::WHITE ? "♗" : "♝"), color) {
+}
+
+bool CBishop::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+
+  int dirX = destX - fromX;
+  int dirY = destY - fromY;
+
+  if(abs(dirX) != abs(dirY))
+    return false;
+
+  // check intersection
+  if(pField->intersect(fromX, fromY, destX, destY))
+    return false;
+
+  return true;
+}

+ 16 - 0
src/pieces/Bishop.h

@@ -0,0 +1,16 @@
+#ifndef PIECE_BISHOP_H
+#define PIECE_BISHOP_H
+
+#include "../Piece.h"
+
+class CBishop : public CPiece {
+
+public:
+
+  CBishop(int color);
+  ~CBishop() { }
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
+};
+
+#endif

+ 52 - 0
src/pieces/King.cpp

@@ -0,0 +1,52 @@
+#include "King.h"
+#include "../Field.h"
+
+CKing::CKing(int color)
+  : CPiece(Type::KING, (color == Color::WHITE ? "♔" : "♚"), color) {
+}
+
+bool CKing::performCasteling(CField *pField, int rookPos) {
+
+  // 1. King may not have been moved yet
+  if(m_HasMoved)
+    return false;
+
+  int kingPos = 4;
+  int baseRow = getColor() == Color::WHITE ? 0 : 7;
+
+  // 2. Rook may not have been moved yet
+  CPiece *piece = pField->getPiece(rookPos, baseRow);
+  if(piece == nullptr || piece->getType() != Type::ROOK || piece->hasMoved())
+    return false;
+
+
+  for(int x = kingPos; (rookPos == 0 ? x >= 0 : x <= 7); (rookPos == 0 ? x-- : x++)) {
+
+    // 3. Empty fields between king and rook
+    if(x != rookPos && x != kingPos) {
+      if(pField->getPiece(x, baseRow) != nullptr) {
+        return false;
+      }
+    }
+
+    // 4. No fields are attacked
+    if(pField->isAttacked(x, baseRow, 1 - getColor())) {
+      return false;
+    }
+  }
+
+  // Swap!
+  pField->swapPieces(kingPos, baseRow, rookPos, baseRow);
+  m_HasMoved = true;
+  piece->m_HasMoved = true;
+  return true;
+}
+
+bool CKing::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+  int dirX = destX - fromX;
+  int dirY = destY - fromY;
+
+  return abs(dirX) <= 1 && abs(dirY) <= 1;
+
+}

+ 21 - 0
src/pieces/King.h

@@ -0,0 +1,21 @@
+#ifndef PIECE_KING_H
+#define PIECE_KING_H
+
+#include "../Piece.h"
+
+class CKing : public CPiece {
+
+  bool performCasteling(class CField *pField, int rookPos);
+
+public:
+
+  CKing(int color);
+  ~CKing() { }
+
+  bool performKingsideCasteling(class CField *pField) { return performCasteling(pField, 7); };
+  bool performQueensideCasteling(class CField *pField) { return performCasteling(pField, 0); };
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
+};
+
+#endif

+ 14 - 0
src/pieces/Knight.cpp

@@ -0,0 +1,14 @@
+#include "Knight.h"
+
+CKnight::CKnight(int color)
+  : CPiece(Type::KNIGHT, (color == Color::WHITE ? "♘" : "♞"), color) {
+}
+
+bool CKnight::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+  int dirX = destX - fromX;
+  int dirY = destY - fromY;
+
+  // 1 step in first direction, 2 steps in second direction
+  return ((abs(dirX) == 2 && abs(dirY) == 1) || (abs(dirX) == 1 && abs(dirY) == 2));
+}

+ 16 - 0
src/pieces/Knight.h

@@ -0,0 +1,16 @@
+#ifndef PIECE_KNIGHT_H
+#define PIECE_KNIGHT_H
+
+#include "../Piece.h"
+
+class CKnight : public CPiece {
+
+public:
+
+  CKnight(int color);
+  ~CKnight() { }
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
+};
+
+#endif

+ 41 - 2
src/pieces/Pawn.cpp

@@ -1,6 +1,45 @@
 #include "Pawn.h"
+#include "../Field.h"
 
 CPawn::CPawn(int color)
-  : CPiece((color == Color::WHITE ? "♙" : "♟"), color) {
-    hasMoved = false;
+  : CPiece(Type::PAWN, (color == Color::WHITE ? "♙" : "♟"), color) {
+}
+
+bool CPawn::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+  // check for valid movement
+  int dirY = destY - fromY;
+  int dirX = destX - fromX;
+
+  // dirY = { -2, -1, 1, 2 }
+  // dirX = { -1, 0, 1     }
+
+  // Invalid movement
+  if(abs(dirY) > 2 || abs(dirX) > 1 || dirY == 0)
+    return false;
+
+  // Check for direction
+  if(dirY < 0 && getColor() == Color::WHITE)
+    return false;
+  else if(dirY > 0 && getColor() == Color::BLACK)   // Check for direction
+    return false;
+
+  // Case 1: 2 steps if not moved
+  if(abs(dirY) == 2) {
+    if(dirX != 0 || m_HasMoved)
+      return false;
+
+  // Case 2: 1 Step crosswise
+  } else if(abs(dirY) == 1 && dirX != 0) {
+      CPiece *targetPiece = pField->getPiece(fromX + dirX, fromY + dirY);
+      if(targetPiece == nullptr)
+        return false;
+
+      // TODO: en passant
+      // else
+  }
+
+  // Todo: check for base line of opponent
+
+  return !pField->intersect(fromX, fromY, destX, destY, dirX == 0);
 }

+ 2 - 2
src/pieces/Pawn.h

@@ -5,12 +5,12 @@
 
 class CPawn : public CPiece {
 
-  bool hasMoved;
-
 public:
 
   CPawn(int color);
   ~CPawn() { }
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
 };
 
 #endif

BIN
src/pieces/Pawn.h.gch


+ 17 - 0
src/pieces/Queen.cpp

@@ -0,0 +1,17 @@
+#include "Queen.h"
+#include "../Field.h"
+
+CQueen::CQueen(int color)
+  : CPiece(Type::QUEEN, (color == Color::WHITE ? "♕" : "♛"), color) {
+}
+
+bool CQueen::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+  int dirX = destX - fromX;
+  int dirY = destY - fromY;
+
+  if(abs(dirX) == abs(dirY) || ((fromX == destX) != (fromY == destY)))
+    return !pField->intersect(fromX, fromY, destX, destY);
+
+  return false;
+}

+ 16 - 0
src/pieces/Queen.h

@@ -0,0 +1,16 @@
+#ifndef PIECE_QUEEN_H
+#define PIECE_QUEEN_H
+
+#include "../Piece.h"
+
+class CQueen : public CPiece {
+
+public:
+
+  CQueen(int color);
+  ~CQueen() { }
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
+};
+
+#endif

+ 19 - 0
src/pieces/Rook.cpp

@@ -0,0 +1,19 @@
+#include "Rook.h"
+#include "../Field.h"
+
+CRook::CRook(int color)
+  : CPiece(Type::ROOK, (color == Color::WHITE ? "♖" : "♜"), color) {
+}
+
+bool CRook::canMove(CField *pField, int fromX, int fromY, int destX, int destY) {
+
+  // different x or different y but not both different/equal
+  if((fromX == destX) == (fromY == destY))
+    return false;
+
+  // check intersection
+  if(pField->intersect(fromX, fromY, destX, destY))
+    return false;
+
+  return true;
+}

+ 16 - 0
src/pieces/Rook.h

@@ -0,0 +1,16 @@
+#ifndef PIECE_ROOK_H
+#define PIECE_ROOK_H
+
+#include "../Piece.h"
+
+class CRook : public CPiece {
+
+public:
+
+  CRook(int color);
+  ~CRook() { }
+
+  virtual bool canMove(class CField *pField, int fromX, int fromY, int destX, int destY);
+};
+
+#endif