From d73c93b4c783edfc6f3afe19d35b9409953c7323 Mon Sep 17 00:00:00 2001
From: Stephen Enders <smenders@gmail.com>
Date: Wed, 27 Jan 2021 22:15:08 -0500
Subject: Legal placement logic and capturing

Use board_t for board and prevent illegal placements. And allow for
piece capture
---
 src/helper.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 src/helper.hpp | 25 +++++++++++++++++--
 src/ur.cpp     | 72 +++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 142 insertions(+), 34 deletions(-)

diff --git a/src/helper.cpp b/src/helper.cpp
index 96eb919..11374e8 100644
--- a/src/helper.cpp
+++ b/src/helper.cpp
@@ -36,10 +36,10 @@ next(int* p, int max)
   return i;
 }
 
-std::shared_ptr<std::vector<struct piece_t>>
+std::shared_ptr<std::vector<struct board_t>>
 createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
 {
-  auto board = std::make_shared<std::vector<struct piece_t>>();
+  auto board = std::make_shared<std::vector<struct board_t>>();
   sf::Texture& star_texture = (*textures)[STAR_TILE];
   int blank_idx = 0;
   int sp_idx = 0;
@@ -53,12 +53,12 @@ createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
   // p1 pieces
   // p1 star
   // id's don't matter
-  int id = 0;
   {
     sf::Sprite s;
     s.setTexture(star_texture);
     s.setPosition(pos(3, 5));
-    board->push_back({ id++, 3, s });
+    struct board_t bp = { P1_ID, 3, s };
+    board->push_back(bp);
   }
   // p1 start
   for (int i = 0; i < 3; i++) {
@@ -66,25 +66,29 @@ createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
     sf::Sprite s;
     s.setTexture(t);
     s.setPosition(pos(4 + i, 5));
-    board->push_back({ id++, 2 - i, s });
+    struct board_t bp = { P1_ID, 2 - i, s };
+    board->push_back(bp);
   }
   // p1 end
   {
     sf::Sprite goal;
     goal.setTexture((*textures)[P1_END]);
     goal.setPosition(pos(8, 5));
-    board->push_back({ id++, EXIT_SPACE, goal });
+    struct board_t exit_p = { P1_ID, EXIT_SPACE, goal };
+    board->push_back(exit_p);
 
     sf::Sprite end_star;
     end_star.setTexture(star_texture);
     end_star.setPosition(pos(9, 5));
-    board->push_back({ id++, EXIT_SPACE - 1, end_star });
+    struct board_t end_p = { P1_ID, EXIT_SPACE - 1, end_star };
+    board->push_back(end_p);
 
     sf::Texture& t = (*textures)[P1_BOARD_TILES[next(&sp_idx, 2)]];
     sf::Sprite s;
     s.setTexture(t);
     s.setPosition(pos(10, 5));
-    board->push_back({ id++, EXIT_SPACE - 2, s });
+    struct board_t bp = { P1_ID, EXIT_SPACE - 2, s };
+    board->push_back(bp);
   }
   // center pieces
   for (int i = 0; i < 8; i++) {
@@ -96,7 +100,8 @@ createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
       s.setTexture(t);
     }
     s.setPosition(pos(3 + i, 4));
-    board->push_back({ id++, 4 + i, s });
+    struct board_t bp = { SHARED_ID, 4 + i, s };
+    board->push_back(bp);
   }
   // p2 pieces
   // p2 star
@@ -104,7 +109,8 @@ createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
     sf::Sprite s;
     s.setTexture(star_texture);
     s.setPosition(pos(3, 3));
-    board->push_back({ id++, 3, s });
+    struct board_t bp = { P2_ID, 3, s };
+    board->push_back(bp);
   }
   // p2 start
   for (int i = 0; i < 3; i++) {
@@ -112,25 +118,29 @@ createBoard(std::shared_ptr<std::vector<sf::Texture>> textures)
     sf::Sprite s;
     s.setTexture(t);
     s.setPosition(pos(4 + i, 3));
-    board->push_back({ id++, 2 - i, s });
+    struct board_t bp = { P2_ID, 2 - i, s };
+    board->push_back(bp);
   }
   // p2 end
   {
     sf::Sprite goal;
     goal.setTexture((*textures)[P2_END]);
     goal.setPosition(pos(8, 3));
-    board->push_back({ id++, EXIT_SPACE, goal });
+    struct board_t goal_p = { P2_ID, EXIT_SPACE, goal };
+    board->push_back(goal_p);
 
     sf::Sprite end_star;
     end_star.setTexture(star_texture);
     end_star.setPosition(pos(9, 3));
-    board->push_back({ id++, EXIT_SPACE - 1, end_star });
+    struct board_t end_p = { P2_ID, EXIT_SPACE - 1, end_star };
+    board->push_back(end_p);
 
     sf::Texture& t = (*textures)[P2_BOARD_TILES[next(&sp_idx, 2)]];
     sf::Sprite end_tile;
     end_tile.setTexture(t);
     end_tile.setPosition(pos(10, 3));
-    board->push_back({ id++, EXIT_SPACE - 2, end_tile });
+    struct board_t bp = { P2_ID, EXIT_SPACE - 2, end_tile };
+    board->push_back(bp);
   }
 
   return board;
@@ -161,9 +171,10 @@ createPiece(int id, sf::Texture& texture)
 }
 
 std::shared_ptr<struct player_t>
-createPlayer(sf::Texture& texture)
+createPlayer(const int pid, sf::Texture& texture)
 {
   std::shared_ptr<struct player_t> player = std::make_shared<struct player_t>();
+  player->pid = pid;
   player->score = 0;
   player->pieces = std::make_shared<std::vector<struct piece_t>>();
   for (int i = 0; i < NUM_PIECES; i++) {
@@ -273,3 +284,41 @@ pos(float c, float r)
 {
   return { c * SPRITE_SIZE, r * SPRITE_SIZE };
 }
+
+bool
+canPlace(struct piece_t* piece,
+         int turn_pid,
+         struct board_t board_tile,
+         std::shared_ptr<std::vector<struct piece_t>> myPieces,
+         std::shared_ptr<std::vector<struct piece_t>> opponentPieces,
+         int& takenPieceId)
+{
+  if (board_tile.pid != turn_pid && board_tile.pid != SHARED_ID)
+    return false;
+  int position = board_tile.position;
+  if (position == EXIT_SPACE)
+    return true; // can always place in the exit
+
+  // can never collide with your own pieces
+  for (auto& p : (*myPieces)) {
+    if ((p.id != piece->id) // not the same
+        && (p.position == position)) {
+      return false; // collides with own pieces
+    }
+  }
+  // can collide with an enemy
+  if (position >= 4 && position <= 11) {
+    for (auto& p : (*opponentPieces)) {
+      // collided
+      if (p.position == position) {
+        // can not capture on safe space
+        if (position == SAFE_SPACE)
+          return false;
+        takenPieceId = p.id;
+        return true;
+      }
+    }
+  }
+
+  return true;
+}
diff --git a/src/helper.hpp b/src/helper.hpp
index 9495791..2ff8b24 100644
--- a/src/helper.hpp
+++ b/src/helper.hpp
@@ -16,6 +16,9 @@ static const float SPRITE_ROWS = 9.f;
 static const float SPRITE_COLS = 14.f;
 static const float SCR_W = SPRITE_SIZE / ZOOM * SPRITE_COLS / ZOOM;
 static const float SCR_H = SPRITE_SIZE / ZOOM * SPRITE_ROWS / ZOOM;
+static const int SHARED_ID = -1;
+static const int P1_ID = 0;
+static const int P2_ID = 1;
 static const int P1_PIECE = 19;
 static const int P2_PIECE = 18;
 static const int P1_BOARD_TILES[2] = { 0, 1 };
@@ -45,10 +48,19 @@ struct piece_t
   int id;
   int position;
   sf::Sprite sprite;
+  sf::Vector2f origin;
+};
+
+struct board_t
+{
+  int pid; // owning players space -1 for shared
+  int position;
+  sf::Sprite sprite;
 };
 
 struct player_t
 {
+  int pid;
   int score;
   std::shared_ptr<std::vector<struct piece_t>> pieces;
 };
@@ -63,14 +75,14 @@ struct dice_t
 std::shared_ptr<std::vector<sf::Texture>>
 loadTextures(const char* path);
 
-std::shared_ptr<std::vector<struct piece_t>>
+std::shared_ptr<std::vector<struct board_t>>
 createBoard(std::shared_ptr<std::vector<sf::Texture>> textures);
 
 sf::Font
 loadFont();
 
 std::shared_ptr<struct player_t>
-createPlayer(sf::Texture& pieceTexture);
+createPlayer(const int pid, sf::Texture& pieceTexture);
 
 std::shared_ptr<struct piece_t>
 createPiece(int id, sf::Texture& texture);
@@ -101,4 +113,13 @@ getLegalMoves(std::shared_ptr<struct player_t> activePlayer,
 
 sf::Vector2f
 pos(float c, float r);
+
+bool
+canPlace(struct piece_t* piece,
+         int turn_pid,
+         struct board_t board_tile,
+         std::shared_ptr<std::vector<struct piece_t>> myPieces,
+         std::shared_ptr<std::vector<struct piece_t>> opponentPieces,
+         int& takenPieceId);
+
 #endif
diff --git a/src/ur.cpp b/src/ur.cpp
index 6bd27f4..f406623 100644
--- a/src/ur.cpp
+++ b/src/ur.cpp
@@ -30,8 +30,8 @@ change_state(GameState next)
   state = next;
 }
 
-// p1 = false, p2 = true
-bool turn_tracker = false;
+// tracks the turn pids
+int turn_pid = P1_ID;
 int rolling_frame = 0;
 
 inline void
@@ -43,20 +43,20 @@ next(int* i, int max)
 inline void
 next_turn()
 {
-  turn_tracker = !turn_tracker;
+  turn_pid = turn_pid == P1_ID ? P2_ID : P1_ID;
   change_state(GameState::WAITING);
 }
 
 inline bool
 p1_turn()
 {
-  return !turn_tracker;
+  return turn_pid == P1_ID;
 }
 
 inline bool
 p2_turn()
 {
-  return turn_tracker;
+  return turn_pid == P2_ID;
 }
 
 inline void
@@ -160,14 +160,14 @@ main()
   const std::shared_ptr<std::vector<sf::Texture>> textures =
     loadTextures(TEXTURE_PATH);
 
-  const std::shared_ptr<std::vector<struct piece_t>> board =
+  const std::shared_ptr<std::vector<struct board_t>> board =
     createBoard(textures);
 
   const std::shared_ptr<struct player_t> p1 =
-    createPlayer((*textures)[P1_PIECE]);
+    createPlayer(P1_ID, (*textures)[P1_PIECE]);
 
   const std::shared_ptr<struct player_t> p2 =
-    createPlayer((*textures)[P2_PIECE]);
+    createPlayer(P2_ID, (*textures)[P2_PIECE]);
 
   const std::shared_ptr<std::vector<sf::Sprite>> roll_sprites =
     createRollSprites((*textures)[ROLL_TILES[0]], (*textures)[ROLL_TILES[1]]);
@@ -187,11 +187,13 @@ main()
   int p_num = (SPRITE_COLS / 2) - (p1->pieces->size() / 2) - 1;
   for (auto& p : *(p1->pieces)) {
     p.sprite.setPosition(pos(p_num++, SPRITE_ROWS - 1));
+    p.origin = p.sprite.getPosition();
   }
 
   p_num = (SPRITE_COLS / 2) - (p2->pieces->size() / 2) - 1;
   for (auto& p : *(p2->pieces)) {
     p.sprite.setPosition(pos(p_num++, 0));
+    p.origin = p.sprite.getPosition();
   }
 
   sf::Sprite roll_result;
@@ -263,6 +265,15 @@ main()
         window.setView(window.getDefaultView()); // reset back to main view
       } else if (!sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
         mouse_left_locked = false;
+        // is a piece being clicked
+        std::shared_ptr<std::vector<struct piece_t>> pieces, enemyPieces;
+        if (p1_turn()) {
+          pieces = p1->pieces;
+          enemyPieces = p2->pieces;
+        } else {
+          pieces = p2->pieces;
+          enemyPieces = p1->pieces;
+        }
         if (state == GameState::PLACING && grabbed_piece != nullptr) {
           // did the piece drop into place
           bool in_place = false;
@@ -274,9 +285,27 @@ main()
               if (intersect.width > SPRITE_SIZE / 2 &&
                   intersect.height > SPRITE_SIZE / 2) {
                 // check valid placement
-                grabbed_piece->sprite.setPosition(s.getPosition());
-                in_place = true;
-                break;
+                int takenPieceId = -1;
+                if (canPlace(grabbed_piece,
+                             turn_pid,
+                             bp,
+                             pieces,
+                             enemyPieces,
+                             takenPieceId)) {
+                  // did we take a piece
+                  if (takenPieceId >= 0) {
+                    for (auto& ep : (*enemyPieces)) {
+                      if (ep.id = takenPieceId) {
+                        ep.sprite.setPosition(ep.origin);
+                        ep.position = -1;
+                      }
+                    }
+                  }
+                  grabbed_piece->sprite.setPosition(s.getPosition());
+                  grabbed_piece->position = bp.position;
+                  in_place = true;
+                  break;
+                }
               }
             }
           }
@@ -308,12 +337,21 @@ main()
       float y = mPos.y - (grabbed_piece->sprite.getGlobalBounds().height / 2);
       grabbed_piece->sprite.setPosition(x, y);
     }
-    // draw unused pieces
-    for (auto& p : *(p1->pieces)) {
-      window.draw(p.sprite);
-    }
-    for (auto& p : *(p2->pieces)) {
-      window.draw(p.sprite);
+    // draw pieces (draw own pieces last to ensure "on top")
+    if (p1_turn()) {
+      for (auto& p : *(p2->pieces)) {
+        window.draw(p.sprite);
+      }
+      for (auto& p : *(p1->pieces)) {
+        window.draw(p.sprite);
+      }
+    } else {
+      for (auto& p : *(p1->pieces)) {
+        window.draw(p.sprite);
+      }
+      for (auto& p : *(p2->pieces)) {
+        window.draw(p.sprite);
+      }
     }
 
     render_dice(&window,
-- 
cgit v1.2.3-54-g00ecf