Procházet zdrojové kódy

warnings for invalid moves, option to reselect while moving, force
moving until it is not possible

Kajetan Johannes Hammerle před 4 roky
rodič
revize
a72d83a486
2 změnil soubory, kde provedl 191 přidání a 49 odebrání
  1. 160 44
      Game.cpp
  2. 31 5
      Game.h

+ 160 - 44
Game.cpp

@@ -3,7 +3,7 @@
 #include "Game.h"
 #include "Types.h"
 
-Game::Game() : activeX(-1), activeY(-1) {
+Game::Game() : activeX(-1), activeY(-1), directionX(0), directionY(0), state(SELECTION), lastLocation(0) {
     reset();
 }
 
@@ -12,14 +12,14 @@ void Game::reset() {
     activeY = -1;
     for(uint x = 0; x < 9; x++) {
         for(uint y = 0; y < 5; y++) {
-            fields[x][y] = (y > 2 ? FieldState::WHITE : FieldState::BLACK);
+            fields[x][y] = (y > 2 ? WHITE : BLACK);
         }
     }
-    fields[0][2] = FieldState::WHITE;
-    fields[2][2] = FieldState::WHITE;
-    fields[4][2] = FieldState::EMPTY;
-    fields[5][2] = FieldState::WHITE;
-    fields[7][2] = FieldState::WHITE;
+    fields[0][2] = WHITE;
+    fields[2][2] = WHITE;
+    fields[4][2] = EMPTY;
+    fields[5][2] = WHITE;
+    fields[7][2] = WHITE;
 }
 
 void Game::reset(String& output) {
@@ -63,59 +63,138 @@ void Game::printLine(String& s, int index) const {
     s.append("\n\r");
 }
 
-void Game::parse(const String& input, String& output) {
+bool Game::parseLocation(const String& input, int& x, int& y) {
     if(input.getLength() <= 2 || !isDigit(input[0]) || input[1] != ' ' || !isDigit(input[2])) {
-        revertToSelection(output);
-        return;
-    }
-    uint x = input[0] - '0';
-    uint y = input[2] - '0';
-    if(x >= 9 || y >= 6) {
-        revertToSelection(output);
-        return;
-    }
-    if(activeX == -1 || activeY == -1) {
-        markActive(x, y, output);
-    } else {
-        move(x, y, output);
+        return true;
     }
+    x = input[0] - '0';
+    y = input[2] - '0';
+    return x >= 9 || y >= 6;
 }
 
-bool Game::isDigit(char c) const {
-    return c >= '0' && c <= '9';
+void Game::parse(const String& input, String& output) {
+    switch(state) {
+        case SELECTION:
+            parseSelection(input, output);
+            break;
+        case MOVE:
+            parseMove(input, output);
+            break;
+        case ANOTHER_MOVE:
+            parseAnotherMove(input, output);
+            break;
+    }
 }
 
-void Game::markActive(int x, int y, String& output) {
-    if(fields[x][y] != FieldState::WHITE) {
-        revertToSelection(output);
+void Game::parseSelection(const String& input, String& output) {
+    int x;
+    int y;
+    if(parseLocation(input, x, y) || fields[x][y] != WHITE) {
+        output.append("please choose white stone.\n\r");
+        print(output);
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rHuman turn\n\rselect stone: ");
         return;
     }
+    state = MOVE;
     activeX = x;
     activeY = y;
     print(output);
     output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
 }
 
-void Game::move(int x, int y, String& output) {
-    if(fields[x][y] != FieldState::EMPTY || !areNeighbours(activeX, activeY, x, y)) {
-        revertToSelection(output);
+void Game::parseMove(const String& input, String& output) {
+    int x;
+    int y;
+    if(parseLocation(input, x, y)) {
+        output.append("invalid move location.\n\r");
+        print(output);
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
+        return;
+    }
+    if(fields[x][y] == WHITE) {
+        activeX = x;
+        activeY = y;
+        print(output);
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
+        return;
+    }
+    if(fields[x][y] != EMPTY || !areNeighbours(activeX, activeY, x, y)) {
+        output.append("invalid move location.\n\r");
+        print(output);
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
+        return;
+    }
+    lastLocation = 0;
+    move(activeX, activeY, x, y);
+
+    bool removed = false;
+    removed = removeLine(x, y, activeX, activeY, BLACK) || removed;
+    removed = removeLine(activeX, activeY, x, y, BLACK) || removed;
+
+    activeX = x;
+    activeY = y;
+    print(output);
+    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\r");
+    print(output);
+    if(removed && isAnotherTurnPossible()) {
+        state = ANOTHER_MOVE;
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
+        return;
+    }
+    state = SELECTION;
+    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rAI turn\n\r");
+    activeX = -1;
+    activeY = -1;
+    botMove(output);
+}
+
+void Game::parseAnotherMove(const String& input, String& output) {
+    int x;
+    int y;
+    if(parseLocation(input, x, y) || !canMoveActiveStoneTo(x - activeX, y - activeY)) {
+        output.append("invalid move location.\n\r");
+        print(output);
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
         return;
     }
-    fields[activeX][activeY] = FieldState::EMPTY;
-    fields[x][y] = FieldState::WHITE;
-    removeLine(x, y, activeX, activeY, FieldState::BLACK);
-    removeLine(activeX, activeY, x, y, FieldState::BLACK);
+    move(activeX, activeY, x, y);
+
+    bool removed = false;
+    removed = removeLine(x, y, activeX, activeY, BLACK) || removed;
+    removed = removeLine(activeX, activeY, x, y, BLACK) || removed;
+
     activeX = x;
     activeY = y;
     print(output);
     output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\r");
     print(output);
+    if(removed && isAnotherTurnPossible()) {
+        output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
+        return;
+    }
+    state = SELECTION;
     output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rAI turn\n\r");
     activeX = -1;
     activeY = -1;
     botMove(output);
 }
 
+void Game::move(int fromX, int fromY, int toX, int toY) {
+    addLocation(fromX, fromY);
+    directionX = toX - fromX;
+    directionY = toY - fromY;
+    fields[fromX][fromY] = EMPTY;
+    fields[toX][toY] = WHITE;
+}
+
+bool Game::isDigit(char c) const {
+    return c >= '0' && c <= '9';
+}
+
+bool Game::isInRange(int x, int y) const {
+    return x >= 0 && x <= 8 && y >= 0 && y <= 4;
+}
+
 bool Game::areNeighbours(int x, int y, int x2, int y2) const {
     if(x == x2 && y == y2) {
         return false;
@@ -138,16 +217,18 @@ void Game::revertToSelection(String& output) {
     output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rHuman turn\n\rselect stone: ");
 }
 
-void Game::removeLine(int x, int y, int x2, int y2, FieldState remove) {
+bool Game::removeLine(int x, int y, int x2, int y2, FieldState remove) {
     int diffX = x2 - x;
     int diffY = y2 - y;
+    bool removed = false;
     while(true) {
         x2 += diffX;
         y2 += diffY;
-        if(x2 < 0 || x2 >= 9 || y2 < 0 || y2 >= 5 || fields[x2][y2] != remove) {
-            return;
+        if(!isInRange(x2, y2) || fields[x2][y2] != remove) {
+            return removed;
         }
-        fields[x2][y2] = FieldState::EMPTY;
+        removed = true;
+        fields[x2][y2] = EMPTY;
     }
 }
 
@@ -158,21 +239,21 @@ void Game::botMove(String& output) {
         while(true) {
             x = rand() % 9;
             y = rand() % 5;
-            if(fields[x][y] == FieldState::EMPTY) {
+            if(fields[x][y] == EMPTY) {
                 break;
             }
         }
         for(uint i = 0; i < 10; i++) {
             int mx = x + (rand() % 3) - 1;
             int my = y + (rand() % 3) - 1;
-            if((mx == x && my == y) || mx < 0 || mx >= 9 || my < 0 || my >= 5 || fields[mx][my] != FieldState::BLACK) {
+            if((mx == x && my == y) || !isInRange(mx, my) || fields[mx][my] != BLACK) {
                 continue;
             }
-            fields[mx][my] = FieldState::EMPTY;
-            fields[x][y] = FieldState::BLACK;
-            removeLine(mx, my, x, y, FieldState::WHITE);
-            removeLine(x, y, mx, my, FieldState::WHITE);
-            
+            fields[mx][my] = EMPTY;
+            fields[x][y] = BLACK;
+            removeLine(mx, my, x, y, WHITE);
+            removeLine(x, y, mx, my, WHITE);
+
             activeX = x;
             activeY = y;
             print(output);
@@ -183,4 +264,39 @@ void Game::botMove(String& output) {
             return;
         }
     }
+}
+
+void Game::addLocation(int x, int y) {
+    if(lastLocation >= lastLocations.size()) {
+        // safely to ignore, such long turns will never happen
+        return;
+    }
+    lastLocations[lastLocation].x = x;
+    lastLocations[lastLocation].y = y;
+    lastLocation++;
+}
+
+bool Game::isInQueue(int x, int y) const {
+    for(uint i = 0; i < lastLocation; i++) {
+        if(lastLocations[i].x == x && lastLocations[i].y == y) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Game::canMoveActiveStoneTo(int dirX, int dirY) const {
+    if(directionX == dirX && directionY == dirY) {
+        return false;
+    }
+    int newX = activeX + dirX;
+    int newY = activeY + dirY;
+    return isInRange(newX, newY) && areNeighbours(activeX, activeY, newX, newY) &&
+            fields[newX][newY] == EMPTY && !isInQueue(newX, newY);
+}
+
+bool Game::isAnotherTurnPossible() const {
+    return canMoveActiveStoneTo(-1, -1) || canMoveActiveStoneTo(0, -1) || canMoveActiveStoneTo(1, -1) ||
+            canMoveActiveStoneTo(-1, 0) || canMoveActiveStoneTo(1, 0) ||
+            canMoveActiveStoneTo(-1, 1) || canMoveActiveStoneTo(0, 1) || canMoveActiveStoneTo(1, 1);
 }

+ 31 - 5
Game.h

@@ -1,6 +1,8 @@
 #ifndef GAME_H
 #define GAME_H
 
+#include <array>
+
 #include "String.h"
 #include "Types.h"
 
@@ -10,27 +12,51 @@ public:
     enum FieldState {
         BLACK, WHITE, EMPTY
     };
-    
+
+    enum State {
+        SELECTION, MOVE, ANOTHER_MOVE
+    };
+
     Game();
-    
+
     void reset(String& output);
     void parse(const String& input, String& output);
 
 private:
+    bool parseLocation(const String& input, int& x, int& y);
+    void parseSelection(const String& input, String& output);
+    void parseMove(const String& input, String& output);
+    void parseAnotherMove(const String& input, String& output);
+
+    void move(int fromX, int fromY, int toX, int toY);
     bool isDigit(char c) const;
+    bool isInRange(int x, int y) const;
     void reset();
     void print(String& s) const;
     void printLine(String& s, int index) const;
-    void markActive(int x, int y, String& output);
-    void move(int x, int y, String& output);
-    void removeLine(int x, int y, int x2, int y2, FieldState remove);
+    bool removeLine(int x, int y, int x2, int y2, FieldState remove);
     void revertToSelection(String& output);
     bool areNeighbours(int x, int y, int x2, int y2) const;
     void botMove(String& output);
     
+    void addLocation(int x, int y);
+    bool isInQueue(int x, int y) const;
+    bool canMoveActiveStoneTo(int dirX, int dirY) const;
+    bool isAnotherTurnPossible() const;
+
     FieldState fields[9][5];
     int activeX;
     int activeY;
+    int directionX;
+    int directionY;
+    State state;
+
+    struct Location {
+        int x;
+        int y;
+    };
+    std::array<Location, 20> lastLocations;
+    uint lastLocation;
 };
 
 #endif