Repository: FedorBrykin/OOP
Pull request #12: Task_2_3_1
Author: login: FedorBrykin
Created: 2025-03-20T16:15:20Z | Merged: 2025-03-26T02:16:38Z



Diff:
diff --git a/Task_2_3_1/.gitignore b/Task_2_3_1/.gitignore
index b63da45..3348a20 100644
--- a/Task_2_3_1/.gitignore
+++ b/Task_2_3_1/.gitignore
@@ -39,4 +39,4 @@ bin/
 .vscode/
 
 ### Mac OS ###
-.DS_Store
\ No newline at end of file
+.DS_Store
diff --git a/Task_2_3_1/build.gradle b/Task_2_3_1/build.gradle
index 84ac9b5..d40ddcc 100644
--- a/Task_2_3_1/build.gradle
+++ b/Task_2_3_1/build.gradle
@@ -34,4 +34,4 @@ jacocoTestReport {
 
 application {
     mainClass = 'ru.nsu.brykin.main.Main'
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/AvoidObstaclesBehavior.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/AvoidObstaclesBehavior.java
new file mode 100644
index 0000000..5cb29f9
--- /dev/null
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/AvoidObstaclesBehavior.java
@@ -0,0 +1,22 @@
+package ru.nsu.brykin.model;
+
+import java.util.List;
+
+/**
+ * Избегание препятствий змейкой-роботом.
+ */
+public class AvoidObstaclesBehavior implements BehaviorStrategy {
+    @Override
+    public Direction chooseDirection(Snake snake, GameModel model) {
+        Point head = snake.getHead();
+
+        for (Direction direction : Direction.values()) {
+            Point newHead = direction.movePoint(head);
+            if (!model.isPointOccupied(newHead)) {
+                return direction;
+            }
+        }
+
+        return Direction.RIGHT;
+    }
+}
\ No newline at end of file
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicFood.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicFood.java
index 7b683d6..5d7633a 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicFood.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicFood.java
@@ -45,4 +45,4 @@ public void spawn(int maxX, int maxY, List<Point> snakeBody) {
     public int getScore() {
         return 1;
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicSnake.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicSnake.java
index 94f010e..4e97392 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicSnake.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BasicSnake.java
@@ -27,24 +27,7 @@ public BasicSnake(int startX, int startY, int initialLength) {
     @Override
     public void move() {
         Point head = getHead();
-        Point newHead = new Point(head.posX, head.posY);
-
-        switch (direction) {
-            case UP:
-                newHead.posY--;
-                break;
-            case DOWN:
-                newHead.posY++;
-                break;
-            case LEFT:
-                newHead.posX--;
-                break;
-            case RIGHT:
-                newHead.posX++;
-                break;
-            default:
-                throw new IllegalStateException("Unexpected direction: " + direction);
-        }
+        Point newHead = direction.movePoint(head);
 
         body.addFirst(newHead);
         body.removeLast();
@@ -82,4 +65,4 @@ public Point getHead() {
     public void setDirection(Direction direction) {
         this.direction = direction;
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BehaviorStrategy.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BehaviorStrategy.java
new file mode 100644
index 0000000..d686696
--- /dev/null
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/BehaviorStrategy.java
@@ -0,0 +1,11 @@
+package ru.nsu.brykin.model;
+
+/**
+ * Стратегия поведения змейки-робота.
+ */
+public interface BehaviorStrategy {
+    /**
+     * Выбор направления движения.
+     */
+    Direction chooseDirection(Snake snake, GameModel model);
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/ChaseFoodBehavior.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/ChaseFoodBehavior.java
new file mode 100644
index 0000000..362dcf7
--- /dev/null
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/ChaseFoodBehavior.java
@@ -0,0 +1,25 @@
+package ru.nsu.brykin.model;
+
+/**
+ * Преследование еды змейкой-роботом.
+ */
+public class ChaseFoodBehavior implements BehaviorStrategy {
+    @Override
+    public Direction chooseDirection(Snake snake, GameModel model) {
+        Point head = snake.getHead();
+        int foodX = model.getFood().getX();
+        int foodY = model.getFood().getY();
+
+        if (head.posX < foodX) {
+            return Direction.RIGHT;
+        } else if (head.posX > foodX) {
+            return Direction.LEFT;
+        } else if (head.posY < foodY) {
+            return Direction.DOWN;
+        } else if (head.posY > foodY) {
+            return Direction.UP;
+        }
+
+        return Direction.RIGHT;
+    }
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Direction.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Direction.java
index 369504b..7d5a180 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Direction.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Direction.java
@@ -4,5 +4,17 @@
  * компас.
  */
 public enum Direction {
-    UP, DOWN, LEFT, RIGHT
+    UP, DOWN, LEFT, RIGHT;
+
+    /**
+     * Возвращает новую точку после движения в текущем направлении.
+     */
+    public Point movePoint(Point point) {
+        return switch (this) {
+            case UP -> new Point(point.posX, point.posY - 1);
+            case DOWN -> new Point(point.posX, point.posY + 1);
+            case LEFT -> new Point(point.posX - 1, point.posY);
+            case RIGHT -> new Point(point.posX + 1, point.posY);
+        };
+    }
 }
\ No newline at end of file
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Food.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Food.java
index 50b4f46..81b5a14 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Food.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Food.java
@@ -25,4 +25,4 @@ public interface Food {
      * "счёт на табло".
      */
     int getScore();
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameConfig.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameConfig.java
index e35e5d6..78f3885 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameConfig.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameConfig.java
@@ -36,4 +36,4 @@ public int getInitialSnakeLength() {
     public int getWinLength() {
         return winLength;
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameModel.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameModel.java
index 36d1760..8ed4177 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameModel.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/GameModel.java
@@ -1,13 +1,15 @@
 package ru.nsu.brykin.model;
 
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * В игру Джон, в игру!.
  */
 public class GameModel {
-    private Snake snake;
-    private Food food;
-    private GameConfig config;
+    private final List<Snake> snakes = new ArrayList<>();
+    private final Food food;
+    private final GameConfig config;
     private boolean isGameOver;
     private boolean isWin;
 
@@ -16,10 +18,17 @@ public class GameModel {
      */
     public GameModel(GameConfig config) {
         this.config = config;
-        snake = new BasicSnake(config.getWidth() / 2, config.getHeight() / 2,
-                config.getInitialSnakeLength());
+
+        snakes.add(new BasicSnake(config.getWidth() / 2, config.getHeight() / 2,
+                config.getInitialSnakeLength()));
+
+        snakes.add(new RobotSnake(5, 5, config.getInitialSnakeLength(), new RandomBehavior()));
+        snakes.add(new RobotSnake(15, 15, config.getInitialSnakeLength(), new ChaseFoodBehavior()));
+        snakes.add(new RobotSnake(10, 10, config.getInitialSnakeLength(),
+                new AvoidObstaclesBehavior()));
+
         food = new BasicFood();
-        food.spawn(config.getWidth(), config.getHeight(), snake.getBody());
+        food.spawn(config.getWidth(), config.getHeight(), getAllSnakeBodies());
     }
 
     /**
@@ -30,38 +39,86 @@ public void update() {
             return;
         }
 
-        snake.move();
-        checkCollision();
+        for (Snake snake : snakes) {
+            if (snake instanceof RobotSnake) {
+                ((RobotSnake) snake).updateDirection(this);
+            }
+            snake.move();
+        }
+
+        checkCollisions();
         checkFood();
         checkWin();
     }
 
+    private List<Point> getAllSnakeBodies() {
+        List<Point> allBodies = new ArrayList<>();
+        for (Snake snake : snakes) {
+            allBodies.addAll(snake.getBody());
+        }
+        return allBodies;
+    }
+
     /**
      * Титаник должен видеть айсберг!.
      */
-    private void checkCollision() {
-        Point head = snake.getHead();
-        if (head.posX < 0 || head.posX >= config.getWidth() || head.posY < 0
-                || head.posY >= config.getHeight()) {
+    private void checkCollisions() {
+        Snake playerSnake = snakes.get(0);
+        Point playerHead = playerSnake.getHead();
+        if (playerHead.posX < 0 || playerHead.posX >= config.getWidth()
+                || playerHead.posY < 0 || playerHead.posY >= config.getHeight()) {
             gameOver();
             return;
         }
-
-        for (Point bodyPart : snake.getBody()) {
-            if (bodyPart.posX == head.posX && bodyPart.posY == head.posY && bodyPart != head) {
+        for (Snake otherSnake : snakes) {
+            if (otherSnake != playerSnake && otherSnake.getBody().contains(playerHead)) {
+                gameOver();
+                return;
+            }
+        }
+        for (Point bodyPart : playerSnake.getBody()) {
+            if (bodyPart != playerHead && bodyPart.equals(playerHead)) {
                 gameOver();
                 return;
             }
         }
+        for (int i = 1; i < snakes.size(); i++) {
+            Snake robotSnake = snakes.get(i);
+            Point robotHead = robotSnake.getHead();
+            if (robotHead.posX < 0 || robotHead.posX >= config.getWidth()
+                    || robotHead.posY < 0 || robotHead.posY >= config.getHeight()) {
+                // Змейка-робот умирает, но игра продолжается
+                snakes.remove(i);
+                i--;
+                continue;
+            }
+            for (Snake otherSnake : snakes) {
+                if (otherSnake != robotSnake && otherSnake.getBody().contains(robotHead)) {
+                    snakes.remove(i);
+                    i--;
+                    break;
+                }
+            }
+
+            for (Point bodyPart : robotSnake.getBody()) {
+                if (bodyPart != robotHead && bodyPart.equals(robotHead)) {
+                    snakes.remove(i);
+                    i--;
+                    break;
+                }
+            }
+        }
     }
 
     /**
      * есть поесть?.
      */
     private void checkFood() {
-        if (snake.getHead().posX == food.getX() && snake.getHead().posY == food.getY()) {
-            snake.grow();
-            food.spawn(config.getWidth(), config.getHeight(), snake.getBody());
+        for (Snake snake : snakes) {
+            if (snake.getHead().posX == food.getX() && snake.getHead().posY == food.getY()) {
+                snake.grow();
+                food.spawn(config.getWidth(), config.getHeight(), getAllSnakeBodies());
+            }
         }
     }
 
@@ -69,9 +126,11 @@ private void checkFood() {
      * победа.
      */
     private void checkWin() {
-        if (snake.getBody().size() >= config.getWinLength()) {
-            isWin = true;
-            gameOver();
+        for (Snake snake : snakes) {
+            if (snake.getBody().size() >= config.getWinLength()) {
+                isWin = true;
+                gameOver();
+            }
         }
     }
 
@@ -99,8 +158,8 @@ public boolean isWin() {
     /**
      * а где?.
      */
-    public Snake getSnake() {
-        return snake;
+    public List<Snake> getSnakes() {
+        return snakes;
     }
 
     /**
@@ -116,4 +175,32 @@ public Food getFood() {
     public GameConfig getConfig() {
         return config;
     }
-}
\ No newline at end of file
+
+    /**
+     * Препятствия.
+     */
+    public List<Point> getObstacles() {
+        List<Point> obstacles = new ArrayList<>();
+        for (Snake snake : snakes) {
+            obstacles.addAll(snake.getBody());
+        }
+        return obstacles;
+    }
+
+    /**
+     * Проверяет, занята ли точка препятствием (границы поля или тела змеек).
+     */
+    public boolean isPointOccupied(Point point) {
+        if (point.posX < 0 || point.posX >= config.getWidth()
+                || point.posY < 0 || point.posY >= config.getHeight()) {
+            return true;
+        }
+        for (Snake snake : snakes) {
+            if (snake.getBody().contains(point)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Point.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Point.java
index d14c380..812f066 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Point.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Point.java
@@ -34,4 +34,4 @@ public boolean equals(Object obj) {
     public int hashCode() {
         return 31 * posX + posY;
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RandomBehavior.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RandomBehavior.java
new file mode 100644
index 0000000..266cefd
--- /dev/null
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RandomBehavior.java
@@ -0,0 +1,16 @@
+package ru.nsu.brykin.model;
+
+import java.util.Random;
+
+/**
+ * Случайное поведение змейки-робота.
+ */
+public class RandomBehavior implements BehaviorStrategy {
+    private final Random random = new Random();
+
+    @Override
+    public Direction chooseDirection(Snake snake, GameModel model) {
+        Direction[] directions = Direction.values();
+        return directions[random.nextInt(directions.length)];
+    }
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RobotSnake.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RobotSnake.java
new file mode 100644
index 0000000..80a57b1
--- /dev/null
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/RobotSnake.java
@@ -0,0 +1,24 @@
+package ru.nsu.brykin.model;
+
+/**
+ * Змейка-робот.
+ */
+public class RobotSnake extends BasicSnake {
+    private final BehaviorStrategy behaviorStrategy;
+
+    /**
+     * Конструктор змейки-робота.
+     */
+    public RobotSnake(int startX, int startY, int initialLength,
+                      BehaviorStrategy behaviorStrategy) {
+        super(startX, startY, initialLength);
+        this.behaviorStrategy = behaviorStrategy;
+    }
+
+    /**
+     * Обновление направления движения.
+     */
+    public void updateDirection(GameModel model) {
+        setDirection(behaviorStrategy.chooseDirection(this, model));
+    }
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Snake.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Snake.java
index e5e2e10..d304f24 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Snake.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/model/Snake.java
@@ -30,4 +30,4 @@ public interface Snake {
      * направление.
      */
     void setDirection(Direction direction);
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/view/GameController.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/view/GameController.java
index c1ed3d5..c3f320d 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/view/GameController.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/view/GameController.java
@@ -15,6 +15,8 @@
 import ru.nsu.brykin.model.GameConfig;
 import ru.nsu.brykin.model.GameModel;
 import ru.nsu.brykin.model.Point;
+import ru.nsu.brykin.model.RobotSnake;
+import ru.nsu.brykin.model.Snake;
 
 
 /**
@@ -32,7 +34,7 @@ public class GameController {
     private Timeline timeline;
     private double cellWidth;
     private double cellHeight;
-    private BooleanProperty gameOver = new SimpleBooleanProperty(false);
+    private final BooleanProperty gameOver = new SimpleBooleanProperty(false);
 
     /**
      * создание мира.
@@ -43,9 +45,11 @@ public void initialize() {
         gameModel = new GameModel(new GameConfig());
         cellWidth = gameCanvas.getWidth() / gameModel.getConfig().getWidth();
         cellHeight = gameCanvas.getHeight() / gameModel.getConfig().getHeight();
+
         gameOverLabel.visibleProperty().bind(gameOver);
         gameCanvas.setFocusTraversable(true);
         gameCanvas.requestFocus();
+
         timeline = new Timeline(new KeyFrame(Duration.millis(200), event -> {
             if (gameModel.isGameOver()) {
                 gameOverLabel.setText(gameModel.isWin() ? "You Win!" : "Game Over");
@@ -69,21 +73,14 @@ public void handleKeyPress(KeyEvent event) {
             return;
         }
 
+        Snake playerSnake = gameModel.getSnakes().get(0);
+
         switch (event.getCode()) {
-            case UP:
-                gameModel.getSnake().setDirection(Direction.UP);
-                break;
-            case DOWN:
-                gameModel.getSnake().setDirection(Direction.DOWN);
-                break;
-            case LEFT:
-                gameModel.getSnake().setDirection(Direction.LEFT);
-                break;
-            case RIGHT:
-                gameModel.getSnake().setDirection(Direction.RIGHT);
-                break;
-            default:
-                throw new IllegalStateException("Unexpected direction");
+            case UP -> playerSnake.setDirection(Direction.UP);
+            case DOWN -> playerSnake.setDirection(Direction.DOWN);
+            case LEFT -> playerSnake.setDirection(Direction.LEFT);
+            case RIGHT -> playerSnake.setDirection(Direction.RIGHT);
+            default -> throw new IllegalStateException("Unexpected direction: " + event.getCode());
         }
     }
 
@@ -92,6 +89,7 @@ public void handleKeyPress(KeyEvent event) {
      */
     private void drawGame() {
         gc.clearRect(0, 0, gameCanvas.getWidth(), gameCanvas.getHeight());
+
         drawSnake();
         drawFood();
     }
@@ -100,10 +98,11 @@ private void drawGame() {
      * питон, который съел слона.
      */
     private void drawSnake() {
-        gc.setFill(Color.GREEN);
-        for (Point point : gameModel.getSnake().getBody()) {
-            gc.fillRect(point.posX * cellWidth,
-                    point.posY * cellHeight, cellWidth, cellHeight);
+        for (Snake snake : gameModel.getSnakes()) {
+            gc.setFill(snake instanceof RobotSnake ? Color.BLUE : Color.GREEN);
+            for (Point point : snake.getBody()) {
+                gc.fillRect(point.posX * cellWidth, point.posY * cellHeight, cellWidth, cellHeight);
+            }
         }
     }
 
@@ -122,4 +121,4 @@ private void drawFood() {
     public BooleanProperty gameOverProperty() {
         return gameOver;
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/java/ru/nsu/brykin/view/StartController.java b/Task_2_3_1/src/main/java/ru/nsu/brykin/view/StartController.java
index 9f28c4b..6dac660 100644
--- a/Task_2_3_1/src/main/java/ru/nsu/brykin/view/StartController.java
+++ b/Task_2_3_1/src/main/java/ru/nsu/brykin/view/StartController.java
@@ -19,4 +19,4 @@ private void startGame() throws Exception {
         stage.setScene(new Scene(root));
         stage.show();
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/main/resources/view/GameView.fxml b/Task_2_3_1/src/main/resources/view/GameView.fxml
index 3680c15..ffaa406 100644
--- a/Task_2_3_1/src/main/resources/view/GameView.fxml
+++ b/Task_2_3_1/src/main/resources/view/GameView.fxml
@@ -7,4 +7,4 @@
 <StackPane fx:id="root" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ru.nsu.brykin.view.GameController">
     <Canvas fx:id="gameCanvas" width="400" height="400" onKeyPressed="#handleKeyPress" />
     <Label fx:id="gameOverLabel" text="Game Over" visible="false" />
-</StackPane>
\ No newline at end of file
+</StackPane>
diff --git a/Task_2_3_1/src/main/resources/view/StartView.fxml b/Task_2_3_1/src/main/resources/view/StartView.fxml
index 31aab0f..3b67aab 100644
--- a/Task_2_3_1/src/main/resources/view/StartView.fxml
+++ b/Task_2_3_1/src/main/resources/view/StartView.fxml
@@ -12,4 +12,4 @@
         </font>
     </Text>
     <Button text="Начать игру" onAction="#startGame" />
-</StackPane>
\ No newline at end of file
+</StackPane>
diff --git a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicFoodTest.java b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicFoodTest.java
index a7be181..e754b95 100644
--- a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicFoodTest.java
+++ b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicFoodTest.java
@@ -26,4 +26,4 @@ void testGetScore() {
         BasicFood food = new BasicFood();
         assertEquals(1, food.getScore());
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicSnakeTest.java b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicSnakeTest.java
index 0187154..68268d9 100644
--- a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicSnakeTest.java
+++ b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/BasicSnakeTest.java
@@ -25,4 +25,4 @@ void testGrow() {
         snake.grow();
         assertEquals(4, snake.getBody().size());
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameConfigTest.java b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameConfigTest.java
index 7a954b3..56b729a 100644
--- a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameConfigTest.java
+++ b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameConfigTest.java
@@ -29,4 +29,4 @@ void testGetWinLength() {
         GameConfig config = new GameConfig();
         assertEquals(10, config.getWinLength());
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameModelTest.java b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameModelTest.java
index f6ed2e2..1f339ef 100644
--- a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameModelTest.java
+++ b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/GameModelTest.java
@@ -15,4 +15,4 @@ void testCollision() {
         }
         assertTrue(model.isGameOver());
     }
-}
\ No newline at end of file
+}
diff --git a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/PointTest.java b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/PointTest.java
index dbc5c0e..3a7b0be 100644
--- a/Task_2_3_1/src/test/java/ru/nsu/brykin/model/PointTest.java
+++ b/Task_2_3_1/src/test/java/ru/nsu/brykin/model/PointTest.java
@@ -15,4 +15,4 @@ void testEquals() {
         assertTrue(p1.equals(p2));
         assertFalse(p1.equals(p3));
     }
-}
\ No newline at end of file
+}


Reviews:
user: login: galinabykova
body: с доп задании говорилось подумать, что происходит при столкновении змеек. Что происходит при таком столкновении у тебя?
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-21T15:32:41Z
user: login: galinabykova
body: ревьюдог)
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-24T14:47:06Z
user: login: galinabykova
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-25T12:09:07Z

Review comments:
diff_hunk: @@ -0,0 +1,46 @@
+package ru.nsu.brykin.model;
+
+import java.util.List;
+
+/**
+ * Избегание препятствий змейкой-роботом.
+ */
+public class AvoidObstaclesBehavior implements BehaviorStrategy {
+    @Override
+    public Direction chooseDirection(Snake snake, GameModel model) {
+        Point head = snake.getHead();
+        List<Point> obstacles = model.getObstacles();
+
+        for (Direction direction : Direction.values()) {
+            Point newHead = moveInDirection(head, direction);
+            if (!isCollision(newHead, model)) {
+                return direction;
+            }
+        }
+
+        return Direction.RIGHT;
+    }
+
+    private Point moveInDirection(Point head, Direction direction) {
+        return switch (direction) {
+            case UP -> new Point(head.posX, head.posY - 1);
+            case DOWN -> new Point(head.posX, head.posY + 1);
+            case LEFT -> new Point(head.posX - 1, head.posY);
+            case RIGHT -> new Point(head.posX + 1, head.posY);
+        };
+    }
+
+    private boolean isCollision(Point point, GameModel model) {
path: Task_2_3_1/src/main/java/ru/nsu/brykin/model/AvoidObstaclesBehavior.java
user: login: galinabykova
body: мне кажется, вместо этого метода логичнее было бы сделать в GameModel метод проверки точки на занятость, ты здесь не используешь ничего из этого класса, но много обращаешься к модели игры
created_at: 2025-03-21T15:27:33Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,46 @@
+package ru.nsu.brykin.model;
+
+import java.util.List;
+
+/**
+ * Избегание препятствий змейкой-роботом.
+ */
+public class AvoidObstaclesBehavior implements BehaviorStrategy {
+    @Override
+    public Direction chooseDirection(Snake snake, GameModel model) {
+        Point head = snake.getHead();
+        List<Point> obstacles = model.getObstacles();
+
+        for (Direction direction : Direction.values()) {
+            Point newHead = moveInDirection(head, direction);
+            if (!isCollision(newHead, model)) {
+                return direction;
+            }
+        }
+
+        return Direction.RIGHT;
+    }
+
+    private Point moveInDirection(Point head, Direction direction) {
path: Task_2_3_1/src/main/java/ru/nsu/brykin/model/AvoidObstaclesBehavior.java
user: login: galinabykova
body: этот метод частично дублирует move из BasicSnake. Можно сделать для Direction перевод точки в новое положение, тогда от дублирования можно будет избавиться
created_at: 2025-03-21T15:29:09Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-21T02:36:30Z
body: |File|Coverage [64.9%]|:x:|
|:-|:-:|:-:|
|[RandomBehavior.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRandomBehavior.java)|100%|:green_apple:|
|[GameConfig.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameConfig.java)|100%|:green_apple:|
|[RobotSnake.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRobotSnake.java)|100%|:green_apple:|
|[Direction.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FDirection.java)|100%|:green_apple:|
|[BasicFood.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicFood.java)|100%|:green_apple:|
|[BasicSnake.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicSnake.java)|93.28%|:green_apple:|
|[GameModel.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameModel.java)|86.6%|:green_apple:|
|[ChaseFoodBehavior.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FChaseFoodBehavior.java)|75%|:x:|
|[Point.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FPoint.java)|74.36%|:x:|
|[AvoidObstaclesBehavior.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FAvoidObstaclesBehavior.java)|70.4%|:x:|
|[StartController.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FStartController.java)|0%|:x:|
|[GameController.java](https://github.com/FedorBrykin/OOP/blob/e7231b088da44a2b4a1eca3f699f2c9d37a9e9e6/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FGameController.java)|0%|:x:|

|Total Project Coverage|63.51%|:x:|
|:-|:-:|:-:|
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-24T04:52:50Z
body: |File|Coverage [66.12%]|:x:|
|:-|:-:|:-:|
|[RandomBehavior.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRandomBehavior.java)|100%|:green_apple:|
|[GameConfig.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameConfig.java)|100%|:green_apple:|
|[RobotSnake.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRobotSnake.java)|100%|:green_apple:|
|[Direction.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FDirection.java)|100%|:green_apple:|
|[BasicFood.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicFood.java)|100%|:green_apple:|
|[BasicSnake.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicSnake.java)|100%|:green_apple:|
|[ChaseFoodBehavior.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FChaseFoodBehavior.java)|90%|:green_apple:|
|[AvoidObstaclesBehavior.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FAvoidObstaclesBehavior.java)|88.24%|:green_apple:|
|[GameModel.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameModel.java)|84.31%|:green_apple:|
|[Point.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FPoint.java)|74.36%|:x:|
|[StartController.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FStartController.java)|0%|:x:|
|[GameController.java](https://github.com/FedorBrykin/OOP/blob/3bba3216d2201d44a72f5313ebb329dc30c2c32e/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FGameController.java)|0%|:x:|

|Total Project Coverage|64.65%|:x:|
|:-|:-:|:-:|
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-24T15:53:37Z
body: |File|Coverage [65.21%]|:x:|
|:-|:-:|:-:|
|[RandomBehavior.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRandomBehavior.java)|100%|:green_apple:|
|[GameConfig.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameConfig.java)|100%|:green_apple:|
|[RobotSnake.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FRobotSnake.java)|100%|:green_apple:|
|[Direction.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FDirection.java)|100%|:green_apple:|
|[BasicFood.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicFood.java)|100%|:green_apple:|
|[BasicSnake.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FBasicSnake.java)|100%|:green_apple:|
|[AvoidObstaclesBehavior.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FAvoidObstaclesBehavior.java)|88.24%|:green_apple:|
|[GameModel.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FGameModel.java)|83.44%|:green_apple:|
|[ChaseFoodBehavior.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FChaseFoodBehavior.java)|75%|:x:|
|[Point.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fmodel%2FPoint.java)|74.36%|:x:|
|[StartController.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FStartController.java)|0%|:x:|
|[GameController.java](https://github.com/FedorBrykin/OOP/blob/3f66716ebf3c9932d594a75585b92e922acd1069/Task_2_3_1%2Fsrc%2Fmain%2Fjava%2Fru%2Fnsu%2Fbrykin%2Fview%2FGameController.java)|0%|:x:|

|Total Project Coverage|63.76%|:x:|
|:-|:-:|:-:|
author_association: NONE