diff --git a/README.md b/README.md
index 5d686e9f..6b62de1b 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,25 @@
# Java Core June 2021
-## *Nikolaev Artem*
+### Andropov Vsevolod
| Number | Solution | Short description
| --- | --- | --- |
-| HW1 | [Console printer](https://github.com/NikolaevArtem/Java_Core_June_2021/tree/master/src/main/java/homework_1) | The app that reads input arguments and prints them, until "error" argument |
+HW1 | [Console printer](./src/main/java/homework_1/Main.java)
[Tests](./src/test/java/homework_1/MainTest.java) | The app that reads input arguments and prints them, until "error" argument |
+HW2.1 | [Traffic Light](./src/main/java/homework_2/traffic_light/)
[Tests](./src/test/java/homework_2/traffic_light/TrafficLightTest.java) | The app that switching colors on different number value (input from keyboard) |
+HW2.2 | [Pyramid Printer](./src/main/java/homework_2/pyramid_printer/)
[Tests](./src/test/java/homework_2/pyramid_printer/PyramidPrinterTest.java) | Printing 'x' Pyramid with the input parameter as Pyramid height |
+HW2.3 | [Random Chars Table](./src/main/java/homework_2/random_chars_table/)
[Tests](./src/test/java/homework_2/random_chars_table/RandomCharsTableTest.java) | The app that creates 2-dimensional array/matrix made of random Character values (from A=65 to Z=90), dimensions set by input from keyboard. Then generating resulting string with even/odd parameter depends on input |
+HW3 | [Immutable Class](./src/main/java/homework_3/ImmutableClass.java) | Create Immutable class: several fields, including reference type; several constructors; method returning a new object. |
+HW4.1 | [Custom File Reader](./src/main/java/homework_4/custom_file_reader/)
[Tests](./src/test/java/homework_4/custom_file_reader/CustomFileReaderTest.java) | App reading data from file and prints to console. Text file path: main/resources/custom_file_reader/. Reading file in 4 different ways (one of them using NIO) executable with methods: run1(), run2(), run3(), run4() |
+HW4.2 | [Singleton](./src/main/java/homework_4/singleton/)
[Tests](./src/test/java/homework_4/singleton/SingletonMenuTest.java) | Singleton App: private constructor and public method getInstance() |
+HW4.3 | [Custom Annotation](./src/main/java/homework_4/custom_annotation/)
[Tests](./src/test/java/homework_4/custom_annotation/ResourcesPathTest.java) | Custom annotation class. |
+HW5.1 | [Power Of Number](./src/main/java/homework_5/power_of_number/)
[Tests](./src/test/java/homework_5/power_of_number/PowerOfNumberTest.java) | Power of number recursion using App. Reads 2 non-negative integer numbers from console and prints out the result of exponentiation. Only for positive integer numbers. On any problem output message will be gotten: "Only 2 non-negative integers are allowed" |
+HW5.2 | [Custom Regex Matcher](./src/main/java/homework_5/custom_regex_matcher/)
[Tests](./src/test/java/homework_5/custom_regex_matcher/CustomRegexMatcherTest.java) | App reads input from keyboard and print true or false for the input matching to the hardcoded regex format. |
+HW6 | [MapProblemsGenerator](./src/main/java/homework_6/map_problems_collision_generator/) | App which has:
1) Class creating 100% HashMap collisions
2) Mutable class with overridden HashCode/Equals. When put MutableClass object into HashMap as key - can't retrieve it (equals works correctly) |
+HW7 | [Kitten to Cat](./src/main/java/homework_7/kitten_to_cat/)
[Tests](./src/test/java/homework_7/kitten_to_cat/KittenToCatTest.java) | App using Functional Interface KittenToCatFunction with abstract method grow(). Realization in Main class through lambda function |
+Course project | [Sea Battle](./src/main/java/course_project/) | Sea Battle console game |
-[Link to markdown giude](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)
+
+
+___
+
+[codingbat profile](https://codingbat.com/done?user=devngrow@gmail.com&tag=1205090974) `done`
diff --git a/build.gradle b/build.gradle
index b91dc843..bbc25319 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,8 +10,16 @@ repositories {
}
dependencies {
- testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
- testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
+ implementation 'org.junit.jupiter:junit-jupiter:5.7.2'
+ implementation 'org.jetbrains:annotations:22.0.0'
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
+
+ // lombok
+ compileOnly 'org.projectlombok:lombok:1.18.20'
+ annotationProcessor 'org.projectlombok:lombok:1.18.20'
+ testCompileOnly 'org.projectlombok:lombok:1.18.20'
+ testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
}
test {
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/src/main/java/course_project/Main.java b/src/main/java/course_project/Main.java
new file mode 100644
index 00000000..f96f25cb
--- /dev/null
+++ b/src/main/java/course_project/Main.java
@@ -0,0 +1,11 @@
+package course_project;
+
+import course_project.services.SeaBattle;
+
+public class Main {
+
+ public static void main(String[] args) {
+ SeaBattle.getInstance().run();
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/field/Battlefield.java b/src/main/java/course_project/gamestuff/field/Battlefield.java
new file mode 100644
index 00000000..58e73316
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/field/Battlefield.java
@@ -0,0 +1,112 @@
+package course_project.gamestuff.field;
+
+import course_project.gamestuff.player.Player;
+import course_project.gamestuff.ships.*;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import static course_project.services.conversation.DialogueMenu.*;
+import static course_project.utils.AroundDestroyedShipMarker.*;
+import static course_project.utils.RandomCellPositionGenerator.generateRandomForCell;
+import static course_project.utils.RandomShipDirectionGenerator.generateRandomDirection;
+import static course_project.utils.ShipOnBattlefieldPlacer.*;
+
+/**
+ * Battlefield is Field for the Player(owner) of this object.
+ */
+public class Battlefield extends Field {
+
+ private final Collection availableShips;
+
+ public Battlefield() {
+ super();
+ this.availableShips = new LinkedList<>();
+ initAvailableShips();
+ }
+
+ private void initAvailableShips() {
+ availableShips.add(new ExtraLargeShip());
+ availableShips.add(new LargeShip());
+ availableShips.add(new LargeShip());
+ availableShips.add(new MiddleShip());
+ availableShips.add(new MiddleShip());
+ availableShips.add(new MiddleShip());
+ availableShips.add(new SmallShip());
+ availableShips.add(new SmallShip());
+ availableShips.add(new SmallShip());
+ availableShips.add(new SmallShip());
+ }
+
+ public Collection getAvailableShips() {
+ return this.availableShips;
+ }
+
+ public boolean addShipOnBattlefield(Ship ship, Cell toCell, boolean isHorizontal) {
+ if (isEnoughSpaceToPlaceShip(toCell, ship.getSize(), isHorizontal)) {
+ return isHorizontal ?
+ addRowOfCells(this, toCell, ship) :
+ addColumnOfCells(this, toCell, ship);
+ }
+ return false;
+ }
+
+ // onDestroy
+ public void availableShipsDecrease(Ship ship) {
+ this.availableShips.remove(ship);
+ }
+
+ public boolean hasAvailableShips() {
+ return countOfAvailableShips() > 0;
+ }
+
+ public int countOfAvailableShips() {
+ return this.availableShips.size();
+ }
+
+ public static void addShipsManually(Player currentPlayer) {
+ printPlayerShipPlacingHelp();
+ currentPlayer.getField().getAvailableShips().forEach(ship -> {
+ currentPlayer.getField().printField();
+ Cell toPos;
+ boolean isHorizontal = true;
+ do {
+ printSetPositionMessage(ship.getName(), ship.getSize());
+ String[] pos = getShipPlacingInput().split(" ");
+ int x = Integer.parseInt(pos[0].substring(1)) - 1;
+ int y = Integer.parseInt(String.valueOf(pos[0].charAt(0) - LOWERCASE_A));
+ toPos = currentPlayer.getField().getCellByPosition(x, y);
+ if (pos.length == 2) {
+ isHorizontal = "h".equals(pos[1]);
+ }
+ } while (!currentPlayer.getField().addShipOnBattlefield(ship, toPos, isHorizontal));
+ }
+ );
+ }
+
+ public static void addShipsAuto(Player currentPlayer) {
+ currentPlayer.getField().getAvailableShips().forEach(ship -> {
+ Cell toPos;
+ boolean isHorizontal;
+ do {
+ toPos = currentPlayer.getField().getCellByPosition(generateRandomForCell(),
+ generateRandomForCell());
+ isHorizontal = generateRandomDirection();
+ } while (!currentPlayer.getField().addShipOnBattlefield(ship, toPos, isHorizontal));
+ }
+ );
+ }
+
+ public void setAroundShipCellsAsHit(Player currentPlayer, Ship ship) {
+ if (ship.getSize() == 1) {
+ fillAroundSmallShip(currentPlayer, ship);
+ } else {
+ if (ship.isHorizontal()) {
+ fillAroundHorizontalShip(currentPlayer, ship);
+ } else {
+ fillAroundVerticalShip(currentPlayer, ship);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/field/Cell.java b/src/main/java/course_project/gamestuff/field/Cell.java
new file mode 100644
index 00000000..31012973
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/field/Cell.java
@@ -0,0 +1,47 @@
+package course_project.gamestuff.field;
+
+import course_project.gamestuff.ships.Ship;
+
+public class Cell {
+
+ private final int x;
+ private final int y;
+ private Ship ref;
+ private boolean hit; // stores state of cell: if motion to this cell was made
+
+ public Cell(int x, int y) {
+ this.x = x;
+ this.y = y;
+ this.ref = null;
+ this.hit = false;
+ }
+
+ public boolean isHit() {
+ return this.hit;
+ }
+
+ public void setHit(boolean hit) {
+ this.hit = hit;
+ }
+
+ public Ship isShipPlacedAtCell() {
+ return this.ref == null ? null : ref;
+ }
+
+ public void placeShipOnCell(Ship ship) {
+ this.ref = ship;
+ }
+
+ public int getX() {
+ return this.x;
+ }
+
+ public int getY() {
+ return this.y;
+ }
+
+ public Ship getRef() {
+ return this.ref;
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/field/Field.java b/src/main/java/course_project/gamestuff/field/Field.java
new file mode 100644
index 00000000..4b5384a5
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/field/Field.java
@@ -0,0 +1,140 @@
+package course_project.gamestuff.field;
+
+import course_project.gamestuff.player.Player;
+
+import static homework_2.Colors.*;
+
+public abstract class Field {
+
+ public static final int FIELD_SIZE = 10;
+ public static final int LOWERCASE_A = 'a';
+ private static final String WATER = BLUE_BG + " " + ANSI_RESET;
+ private static final String DESTROYED = ANSI_RED + "†" + ANSI_RESET;
+ private static final String HIT = ANSI_RED + "X" + ANSI_RESET;
+ private static final String MISS = ANSI_YELLOW + "O" + ANSI_RESET;
+ private static final String SHIP = ANSI_GREEN + "¤" + ANSI_RESET;
+ private static final char FIRST_SYMBOL_TOP_LINE = 'A';
+ private static final String TOP_LINE;
+ private static final String CELL_FORMAT = "%s ";
+ private static final String ROW_NUMBER_FORMAT = "[%-2d]";
+
+ private final Cell[][] cells;
+
+ protected Field() {
+ this.cells = new Cell[FIELD_SIZE][FIELD_SIZE];
+ fillCellsWithIndexes();
+ }
+
+ static {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = FIRST_SYMBOL_TOP_LINE; i < FIRST_SYMBOL_TOP_LINE + FIELD_SIZE; i++) {
+ stringBuilder
+ .append((char) i)
+ .append(" ");
+ }
+ TOP_LINE = stringBuilder.toString().trim();
+ }
+
+ private void fillCellsWithIndexes() {
+ for (int i = 0; i < FIELD_SIZE; i++) {
+ for (int j = 0; j < FIELD_SIZE; j++) {
+ cells[i][j] = new Cell(i, j);
+ }
+ }
+ }
+
+ public void printField() {
+ StringBuilder oneField = new StringBuilder();
+ oneField
+ .append("\t")
+ .append(TOP_LINE)
+ .append("\n");
+ for (int i = 0; i < FIELD_SIZE; i++) {
+ oneField.append(String.format(ROW_NUMBER_FORMAT, i + 1));
+ for (int j = 0; j < FIELD_SIZE; j++) {
+ Cell currentCell = getCellByPosition(i, j);
+ String toPrint = WATER;
+ if (!isCellEmpty(currentCell)) {
+ toPrint = SHIP;
+ }
+ oneField.append(String.format(CELL_FORMAT, toPrint));
+ }
+ oneField.append("\n");
+ }
+ System.out.println(oneField);
+ }
+
+ public void printBothFields(Player currentPlayer, Player enemy) {
+ StringBuilder bothFields = new StringBuilder();
+ bothFields
+ .append("\t")
+ .append(TOP_LINE)
+ .append("\t\t\t\t")
+ .append(TOP_LINE)
+ .append("\n");
+ for (int i = 0; i < FIELD_SIZE; i++) {
+ bothFields.append(String.format(ROW_NUMBER_FORMAT, i + 1));
+ for (int j = 0; j < FIELD_SIZE; j++) {
+ Cell currentPlayerCell = currentPlayer.getField().getCellByPosition(i, j);
+ if (currentPlayerCell.isHit()) {
+ bothFields
+ .append(getCellStateAsString(currentPlayer, currentPlayerCell));
+ } else {
+ if (currentPlayer.getField().isCellEmpty(currentPlayerCell)) {
+ bothFields
+ .append(String.format(CELL_FORMAT, WATER));
+ } else {
+ bothFields
+ .append(String.format(CELL_FORMAT, SHIP));
+ }
+ }
+ }
+ bothFields
+ .append("\t\t")
+ .append(String.format(ROW_NUMBER_FORMAT, i + 1));
+ for (int j = 0; j < FIELD_SIZE; j++) {
+ Cell currentEnemyCell = enemy.getField().getCellByPosition(i, j);
+ if (currentEnemyCell.isHit()) {
+ bothFields
+ .append(getCellStateAsString(enemy, currentEnemyCell));
+ } else {
+ bothFields
+ .append(String.format(CELL_FORMAT, WATER));
+ }
+ }
+ bothFields.append("\n");
+ }
+ System.out.println(bothFields);
+ }
+
+ private String getCellStateAsString(Player currentPlayer, Cell playersCell) {
+ if (currentPlayer.getField().getHit(playersCell)) {
+ if (playersCell.getRef().isShipDestroyed()) {
+ return String.format(CELL_FORMAT, DESTROYED);
+ } else {
+ return String.format(CELL_FORMAT, HIT);
+ }
+ }
+ return String.format(CELL_FORMAT, MISS);
+ }
+
+ public Cell getCellByPosition(int x, int y) {
+ for (Cell[] cellRow : this.cells) {
+ for (Cell cellCol : cellRow) {
+ if (cellCol.getX() == x && cellCol.getY() == y) {
+ return cellCol;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isCellEmpty(Cell currentCell) {
+ return currentCell.isShipPlacedAtCell() == null;
+ }
+
+ public boolean getHit(Cell toCell) {
+ return !isCellEmpty(cells[toCell.getX()][toCell.getY()]);
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/player/Player.java b/src/main/java/course_project/gamestuff/player/Player.java
new file mode 100644
index 00000000..476bc089
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/player/Player.java
@@ -0,0 +1,29 @@
+package course_project.gamestuff.player;
+
+import course_project.gamestuff.field.Battlefield;
+
+public class Player {
+
+ private final boolean computer;
+ private final String name;
+ private final Battlefield battlefield;
+
+ public Player(String name, boolean isComputer) {
+ this.computer = isComputer;
+ this.name = name;
+ this.battlefield = new Battlefield();
+ }
+
+ public boolean isComputer() {
+ return this.computer;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public Battlefield getField() {
+ return this.battlefield;
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/ships/ExtraLargeShip.java b/src/main/java/course_project/gamestuff/ships/ExtraLargeShip.java
new file mode 100644
index 00000000..84899159
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/ships/ExtraLargeShip.java
@@ -0,0 +1,12 @@
+package course_project.gamestuff.ships;
+
+public final class ExtraLargeShip extends Ship {
+
+ private static final int SIZE = 4;
+ private static final String SHIP_NAME = "Battleship";
+
+ public ExtraLargeShip() {
+ super(SIZE, SHIP_NAME);
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/ships/LargeShip.java b/src/main/java/course_project/gamestuff/ships/LargeShip.java
new file mode 100644
index 00000000..9e7fe62f
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/ships/LargeShip.java
@@ -0,0 +1,12 @@
+package course_project.gamestuff.ships;
+
+public final class LargeShip extends Ship {
+
+ private static final int SIZE = 3;
+ private static final String SHIP_NAME = "Destroyer";
+
+ public LargeShip() {
+ super(SIZE, SHIP_NAME);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/course_project/gamestuff/ships/MiddleShip.java b/src/main/java/course_project/gamestuff/ships/MiddleShip.java
new file mode 100644
index 00000000..f6494ccb
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/ships/MiddleShip.java
@@ -0,0 +1,12 @@
+package course_project.gamestuff.ships;
+
+public final class MiddleShip extends Ship {
+
+ private static final int SIZE = 2;
+ private static final String SHIP_NAME = "Submarine";
+
+ public MiddleShip() {
+ super(SIZE, SHIP_NAME);
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/ships/Ship.java b/src/main/java/course_project/gamestuff/ships/Ship.java
new file mode 100644
index 00000000..89ab0948
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/ships/Ship.java
@@ -0,0 +1,52 @@
+package course_project.gamestuff.ships;
+
+import course_project.gamestuff.field.Cell;
+
+public abstract class Ship {
+
+ private final int size;
+ private final String name;
+ private int hitCount;
+ private Cell startCell;
+ private boolean direction = true; // true - horizontal, false - vertical
+
+ protected Ship(int size, String shipName) {
+ this.size = size;
+ this.name = shipName;
+ this.hitCount = 0;
+ this.startCell = null;
+ }
+
+ public boolean isHorizontal() {
+ return this.direction;
+ }
+
+ public void setVertical() {
+ this.direction = false;
+ }
+
+ public Cell getStartCell() {
+ return this.startCell;
+ }
+
+ public void setStartCell(Cell cell) {
+ this.startCell = cell;
+ }
+
+ public int getSize() {
+ return this.size;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public boolean isShipDestroyed() {
+ return this.hitCount == this.size;
+ }
+
+ public void hitCountIncrease() {
+ this.hitCount++;
+ }
+
+}
diff --git a/src/main/java/course_project/gamestuff/ships/SmallShip.java b/src/main/java/course_project/gamestuff/ships/SmallShip.java
new file mode 100644
index 00000000..8d34bd1c
--- /dev/null
+++ b/src/main/java/course_project/gamestuff/ships/SmallShip.java
@@ -0,0 +1,12 @@
+package course_project.gamestuff.ships;
+
+public final class SmallShip extends Ship {
+
+ private static final int SIZE = 1;
+ private static final String SHIP_NAME = "Patrol Boat";
+
+ public SmallShip() {
+ super(SIZE, SHIP_NAME);
+ }
+
+}
diff --git a/src/main/java/course_project/services/SeaBattle.java b/src/main/java/course_project/services/SeaBattle.java
new file mode 100644
index 00000000..3718b98d
--- /dev/null
+++ b/src/main/java/course_project/services/SeaBattle.java
@@ -0,0 +1,26 @@
+package course_project.services;
+
+import course_project.services.conversation.DialogueMenu;
+
+/**
+ * Start of SeaBattle game;
+ */
+public final class SeaBattle {
+
+ private SeaBattle() {
+ }
+
+ private static class SeaBattleHelper {
+ private static final SeaBattle INSTANCE = new SeaBattle();
+ }
+
+ public static SeaBattle getInstance() {
+ return SeaBattleHelper.INSTANCE;
+ }
+
+ public void run() {
+ DialogueMenu.printGreetings();
+ DialogueMenu.printMainMenu();
+ }
+
+}
diff --git a/src/main/java/course_project/services/conversation/DialogueMenu.java b/src/main/java/course_project/services/conversation/DialogueMenu.java
new file mode 100644
index 00000000..c37cdae2
--- /dev/null
+++ b/src/main/java/course_project/services/conversation/DialogueMenu.java
@@ -0,0 +1,258 @@
+package course_project.services.conversation;
+
+import course_project.gamestuff.field.Field;
+import course_project.gamestuff.player.Player;
+import course_project.gamestuff.ships.Ship;
+import course_project.services.logic.Game;
+import homework_4.custom_file_reader.CustomFileReader;
+
+import java.io.PrintStream;
+import java.util.Scanner;
+
+import static course_project.gamestuff.field.Battlefield.addShipsAuto;
+import static course_project.gamestuff.field.Battlefield.addShipsManually;
+import static course_project.gamestuff.field.Field.FIELD_SIZE;
+import static course_project.gamestuff.field.Field.LOWERCASE_A;
+
+/**
+ * Ultimate class for communication with user over the app;
+ */
+@SuppressWarnings("java:S106") // remove showing System.out warning
+public class DialogueMenu {
+
+ private static final String INCORRECT_INPUT_MESSAGE = "Please, input correct variant";
+ private static final String FILLED_FIELD_MESSAGE = "Done! Battlefield is filled ;)";
+ private static final String WELCOME_MESSAGE = "Welcome to Sea Battle Console v0.1!";
+ private static final String QUIT = "quit";
+ private static final String FIRST_SYMBOL_POSITION_REGEX;
+ private static final String DIRECTION_REGEX = "^[v|h]$";
+ private static final String NAME_REGEX = "^[a-zA-Z]{2,}$";
+ private static final Scanner scanner = new Scanner(System.in);
+ private static final PrintStream out = System.out;
+
+ static {
+ FIRST_SYMBOL_POSITION_REGEX = "^[" + Field.LOWERCASE_A + "-" + (char) (LOWERCASE_A + FIELD_SIZE - 1) + "]$";
+ }
+
+ private static Player player;
+ private static Player enemy;
+
+ private DialogueMenu() {
+ }
+
+ public static void printGreetings() {
+ out.println(WELCOME_MESSAGE);
+ }
+
+ public static void printMainMenu() {
+ out.print(
+ "[1] Start game Human vs Computer\n" +
+ "[2] Start game Human vs Human\n" +
+ "[3] Start game Computer vs Computer\n" +
+ "[4] Print rules\n" +
+ "[5] Quit the game\n"
+ );
+ String input = getInput();
+ switch (input) {
+ case "1":
+ startHumanVSComputer();
+ break;
+ case "2":
+ startHumanVSHuman();
+ break;
+ case "3":
+ startComputerVSComputer();
+ break;
+ case "4":
+ printRules();
+ printMainMenu();
+ break;
+ case "5":
+ terminate();
+ break;
+ default:
+ out.println(INCORRECT_INPUT_MESSAGE);
+ printMainMenu();
+ }
+ }
+
+ private static void printRules() {
+ new CustomFileReader("./src/main/resources/sea_battle/", "rules.txt").run2();
+ }
+
+ private static void startComputerVSComputer() {
+ player = new Player("CPU0", true);
+ enemy = new Player("CPU1", true);
+ addShipsAuto(player);
+ addShipsAuto(enemy);
+ new Game(player, enemy).start();
+ }
+
+
+ private static void startHumanVSHuman() {
+ player = new Player(getInputName(), false);
+ enemy = new Player(getInputName(), false);
+ printPlayerMenu(player);
+ waitForSeconds(2);
+ printEmptyLines();
+ printInvitePlayerMessage(enemy);
+ waitForSeconds(1);
+ printPlayerMenu(enemy);
+ waitForSeconds(1);
+ new Game(player, enemy).start();
+ }
+
+ @SuppressWarnings("java:S2142")
+ public static void waitForSeconds(int seconds) {
+ try {
+ Thread.sleep(seconds * 1000L);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ private static void startHumanVSComputer() {
+ player = new Player(getInputName(), false);
+ enemy = new Player("Mozilla", true);
+ printPlayerMenu(player);
+ addShipsAuto(enemy);
+ new Game(player, enemy).start();
+ }
+
+ private static String getInputName() {
+ out.println("My name is SeaBattle, and yours?");
+ while (true) {
+ String name = scanner.nextLine().trim();
+ if (name.matches(NAME_REGEX)) {
+ return name;
+ } else {
+ out.println("Ha-ha, it's impossible to have such name!\n" +
+ "It should consists of at least 2 latin symbols(only), ok?");
+ }
+ }
+ }
+
+ public static void printInvitePlayerMessage(Player player) {
+ out.println(player.getName() + "'s turn!");
+ }
+
+ private static void printPlayerMenu(Player currentPlayer) {
+ out.print("Menu for " + currentPlayer.getName() + ":" + "\n" +
+ "[1] Add ships on the field manually\n" +
+ "[2] Auto adding ships\n");
+ String input = getInput();
+ switch (input) {
+ case "1":
+ addShipsManually(currentPlayer);
+ currentPlayer.getField().printField();
+ out.println(FILLED_FIELD_MESSAGE + "\n");
+ break;
+ case "2":
+ addShipsAuto(currentPlayer);
+ currentPlayer.getField().printField();
+ out.println(FILLED_FIELD_MESSAGE + "\n");
+ break;
+ default:
+ out.println(INCORRECT_INPUT_MESSAGE);
+ printPlayerMenu(currentPlayer);
+ }
+ }
+
+ public static String getPlayerMotionInput() {
+ while (true) {
+ String input = getInput();
+ if (QUIT.equals(input)) {
+ terminate();
+ } else if (isPositionValid(input)) {
+ return input;
+ } else {
+ out.println(INCORRECT_INPUT_MESSAGE);
+ }
+ }
+ }
+
+ public static void printPlayerMotionHelp() {
+ out.println("Input position you want to attack in format: 'a1' or 'b9'\n" +
+ "you can input 'quit' to exit the app anytime you want");
+ }
+
+ public static void printPlayerShipPlacingHelp() {
+ out.println("To place ship just input position from" + "'a1' to " +
+ ((char) (LOWERCASE_A + FIELD_SIZE - 1)) + FIELD_SIZE +
+ "(horizontal direction is default)" +
+ "\nIf you want to set ship vertically type e.g. 'a1 v' or horizontal 'b2 h'" +
+ "\nInput 'quit' to exit the game.");
+ }
+
+ public static String getShipPlacingInput() {
+ while (true) {
+ String input = getInput();
+ if (QUIT.equals(input)) {
+ terminate();
+ } else {
+ String[] strings = input.split(" ");
+ if ((strings.length == 1 && isPositionValid(input)) ||
+ (strings.length == 2 && isPositionValid(strings[0]) && strings[1].matches(DIRECTION_REGEX))) {
+ return input;
+ } else {
+ out.println(INCORRECT_INPUT_MESSAGE);
+ }
+ }
+ }
+ }
+
+ private static boolean isPositionValid(String str) {
+ return String.valueOf(str.charAt(0)).matches(FIRST_SYMBOL_POSITION_REGEX) &&
+ str.substring(1).matches("\\d+") &&
+ Integer.parseInt(str.substring(1)) >= 1 &&
+ Integer.parseInt(str.substring(1)) <= FIELD_SIZE;
+ }
+
+ public static void printSetPositionMessage(String name, int size) {
+ out.print("Set position for " + name + " size of " + size + ": ");
+ }
+
+ private static String getInput() {
+ return scanner.nextLine().trim().toLowerCase();
+ }
+
+ public static void printShipDestroyedMessage(String opponentsName, Ship ship) {
+ out.println("Nice! " + opponentsName + "'s " + ship.getName() + " is destroyed!");
+ }
+
+ public static void printShipHitMessage(String currentPlayerName, String opponentsName) {
+ out.println(currentPlayerName + " hit " + opponentsName + "'s ship!");
+ }
+
+ public static void printMiss() {
+ out.println("MISS!");
+ }
+
+ public static void printShipsLeftMessage(int countOfShipsLeft) {
+ out.println(countOfShipsLeft + " ships left");
+ }
+
+ public static void printSamePositionMessage() {
+ out.println("You've already hit at this position! Try another one: ");
+ }
+
+ public static void printCongratulationsAndQuit(Player currentPlayer, Player opponent) {
+ out.println(currentPlayer.getName() + " WON THE GAME !!!" +
+ "\n" + opponent.getName() + " lose.");
+ terminate();
+ }
+
+ public static void printEmptyLines() {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < FIELD_SIZE * 2; i++) {
+ stringBuilder.append("\n");
+ }
+ out.println(stringBuilder);
+ }
+
+ private static void terminate() {
+ scanner.close();
+ Runtime.getRuntime().exit(0); // James Gosling approved!
+ }
+
+}
diff --git a/src/main/java/course_project/services/logic/Game.java b/src/main/java/course_project/services/logic/Game.java
new file mode 100644
index 00000000..550afb03
--- /dev/null
+++ b/src/main/java/course_project/services/logic/Game.java
@@ -0,0 +1,126 @@
+package course_project.services.logic;
+
+import course_project.gamestuff.field.Battlefield;
+import course_project.gamestuff.field.Cell;
+import course_project.gamestuff.player.Player;
+import course_project.gamestuff.ships.Ship;
+
+import static course_project.services.conversation.DialogueMenu.*;
+import static course_project.utils.RandomCellPositionGenerator.generateRandomForCell;
+
+/**
+ * Game logic provides control through the game process;
+ */
+public class Game {
+
+ private final Player player1;
+ private final Player player2;
+ private Player currentPlayer;
+
+ public Game(Player firstPlayer, Player secondPlayer) {
+ this.player1 = firstPlayer;
+ this.player2 = secondPlayer;
+ currentPlayer = getRandomPlayer();
+ }
+
+ private Player getRandomPlayer() {
+ return Math.random() <= 0.5 ? this.player1 : this.player2;
+ }
+
+ // switch to opponents turn
+ private void swapPlayers() {
+ if (currentPlayer == player1) {
+ this.currentPlayer = this.player2;
+ } else {
+ this.currentPlayer = this.player1;
+ }
+ }
+
+ private Player getEnemy() {
+ return currentPlayer == player1 ? player2 : player1;
+ }
+
+
+ public void start() {
+ while (currentPlayer.getField().hasAvailableShips() && getEnemy().getField().hasAvailableShips()) {
+ if (currentPlayer.isComputer() && getEnemy().isComputer()) {
+ computerMotion(currentPlayer);
+ } else if (!currentPlayer.isComputer() && getEnemy().isComputer()) {
+ humanMotion(currentPlayer);
+ } else if (currentPlayer.isComputer() && !getEnemy().isComputer()) {
+ computerMotion(currentPlayer);
+ } else {
+ waitForSeconds(2);
+ printEmptyLines();
+ printInvitePlayerMessage(currentPlayer);
+ waitForSeconds(3);
+ printPlayerMotionHelp();
+ humanMotion(currentPlayer);
+ }
+ }
+ }
+
+ private void computerMotion(Player currentPlayer) {
+ Cell enemyCell = getEnemy().getField().getCellByPosition(generateRandomForCell(), generateRandomForCell());
+ if (!enemyCell.isHit()) {
+ enemyCell.setHit(true);
+ Battlefield enemyBattlefield = getEnemy().getField();
+ if (!enemyBattlefield.isCellEmpty(enemyCell)) {
+ Ship enemyShip = enemyCell.getRef();
+ enemyShip.hitCountIncrease();
+ if (enemyShip.isShipDestroyed()) {
+ enemyBattlefield.availableShipsDecrease(enemyShip);
+ enemyBattlefield.setAroundShipCellsAsHit(getEnemy(), enemyShip);
+ printShipDestroyedMessage(getEnemy().getName(), enemyShip);
+ if (!enemyBattlefield.hasAvailableShips()) {
+ printCongratulationsAndQuit(currentPlayer, getEnemy());
+ }
+ } else {
+ printShipHitMessage(currentPlayer.getName(), getEnemy().getName());
+ }
+ computerMotion(currentPlayer);
+ } else {
+ swapPlayers();
+ }
+ } else {
+ computerMotion(currentPlayer);
+ }
+ }
+
+ private void humanMotion(Player currentPlayer) {
+ currentPlayer.getField().printBothFields(currentPlayer, getEnemy());
+ String pos = getPlayerMotionInput();
+ Cell enemyCell = getEnemy().getField().getCellByPosition(
+ Integer.parseInt(pos.substring(1)) - 1,
+ Integer.parseInt(String.valueOf(pos.charAt(0) - 97))
+ );
+ if (enemyCell.isHit()) {
+ printSamePositionMessage();
+ humanMotion(currentPlayer);
+ } else {
+ enemyCell.setHit(true);
+ Battlefield enemiesField = getEnemy().getField();
+ if (!enemiesField.isCellEmpty(enemyCell)) {
+ Ship enemyShip = enemyCell.getRef();
+ enemyShip.hitCountIncrease();
+ if (enemyShip.isShipDestroyed()) {
+ enemiesField.setAroundShipCellsAsHit(getEnemy(), enemyShip);
+ enemiesField.availableShipsDecrease(enemyShip);
+ printShipDestroyedMessage(getEnemy().getName(), enemyShip);
+ if (!enemiesField.hasAvailableShips()) {
+ currentPlayer.getField().printBothFields(currentPlayer, getEnemy());
+ printCongratulationsAndQuit(currentPlayer, getEnemy());
+ }
+ printShipsLeftMessage(enemiesField.countOfAvailableShips());
+ } else {
+ printShipHitMessage(currentPlayer.getName(), getEnemy().getName());
+ }
+ humanMotion(currentPlayer);
+ } else {
+ printMiss();
+ swapPlayers();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/course_project/utils/AroundDestroyedShipMarker.java b/src/main/java/course_project/utils/AroundDestroyedShipMarker.java
new file mode 100644
index 00000000..14d36f65
--- /dev/null
+++ b/src/main/java/course_project/utils/AroundDestroyedShipMarker.java
@@ -0,0 +1,120 @@
+package course_project.utils;
+
+import course_project.gamestuff.field.Battlefield;
+import course_project.gamestuff.field.Cell;
+import course_project.gamestuff.field.Field;
+import course_project.gamestuff.player.Player;
+import course_project.gamestuff.ships.Ship;
+
+/**
+ * Functions to fill cells around the destroyed ship.
+ */
+public final class AroundDestroyedShipMarker {
+
+ private AroundDestroyedShipMarker() {}
+
+ public static void fillAroundSmallShip(Player currentPlayer, Ship ship) {
+ Cell shipCell = currentPlayer.getField().getCellByPosition(
+ ship.getStartCell().getX(),
+ ship.getStartCell().getY()
+ );
+ Battlefield battlefield = currentPlayer.getField();
+ markAboveAsHitCell(battlefield, shipCell);
+ markBelowAsHitCell(battlefield, shipCell);
+ markRightAsHitCell(battlefield, shipCell);
+ markLeftAsHitCell(battlefield, shipCell);
+ markDiagonalRightBelowAsHitCell(battlefield, shipCell);
+ markDiagonalLeftBelowAsHitCell(battlefield, shipCell);
+ markDiagonalRightAboveAsHitCell(battlefield, shipCell);
+ markDiagonalLeftAboveAsHitCell(battlefield, shipCell);
+ }
+
+ public static void fillAroundVerticalShip(Player currentPlayer, Ship ship) {
+ int x = ship.getStartCell().getX();
+ int y = ship.getStartCell().getY();
+ Battlefield battlefield = currentPlayer.getField();
+ for (int i = 0; i < ship.getSize(); i++) {
+ Cell currentCell = battlefield.getCellByPosition(x + i, y);
+ if (i == 0) {
+ markAboveAsHitCell(battlefield, currentCell);
+ markDiagonalLeftAboveAsHitCell(battlefield, currentCell);
+ markDiagonalRightAboveAsHitCell(battlefield, currentCell);
+ } else if (i == ship.getSize() - 1) {
+ markDiagonalLeftBelowAsHitCell(battlefield, currentCell);
+ markDiagonalRightBelowAsHitCell(battlefield, currentCell);
+ markBelowAsHitCell(battlefield, currentCell);
+ }
+ markLeftAsHitCell(battlefield, currentCell);
+ markRightAsHitCell(battlefield, currentCell);
+ }
+ }
+
+ public static void fillAroundHorizontalShip(Player currentPlayer, Ship ship) {
+ int x = ship.getStartCell().getX();
+ int y = ship.getStartCell().getY();
+ Battlefield battlefield = currentPlayer.getField();
+ for (int i = 0; i < ship.getSize(); i++) {
+ Cell currentCell = currentPlayer.getField().getCellByPosition(x, y + i);
+ if (i == 0) {
+ markDiagonalLeftAboveAsHitCell(battlefield, currentCell);
+ markLeftAsHitCell(battlefield, currentCell);
+ markDiagonalLeftBelowAsHitCell(battlefield, currentCell);
+ } else if (i == ship.getSize() - 1) {
+ markDiagonalRightAboveAsHitCell(battlefield, currentCell);
+ markRightAsHitCell(battlefield, currentCell);
+ markDiagonalRightBelowAsHitCell(battlefield, currentCell);
+ }
+ markAboveAsHitCell(battlefield, currentCell);
+ markBelowAsHitCell(battlefield, currentCell);
+ }
+ }
+
+ private static void markAboveAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != 0) {
+ battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY()).setHit(true);
+ }
+ }
+
+ private static void markBelowAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != Field.FIELD_SIZE - 1) {
+ battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY()).setHit(true);
+ }
+ }
+
+ private static void markRightAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getY() != Field.FIELD_SIZE - 1) {
+ battlefield.getCellByPosition(currentCell.getX(), currentCell.getY() + 1).setHit(true);
+ }
+ }
+
+ private static void markLeftAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getY() != 0) {
+ battlefield.getCellByPosition(currentCell.getX(), currentCell.getY() - 1).setHit(true);
+ }
+ }
+
+ private static void markDiagonalRightBelowAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != Field.FIELD_SIZE - 1 && currentCell.getY() != Field.FIELD_SIZE - 1) {
+ battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY() + 1).setHit(true);
+ }
+ }
+
+ private static void markDiagonalRightAboveAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != 0 && currentCell.getY() != Field.FIELD_SIZE - 1) {
+ battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY() + 1).setHit(true);
+ }
+ }
+
+ private static void markDiagonalLeftBelowAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != Field.FIELD_SIZE - 1 && currentCell.getY() != 0) {
+ battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY() - 1).setHit(true);
+ }
+ }
+
+ private static void markDiagonalLeftAboveAsHitCell(Battlefield battlefield, Cell currentCell) {
+ if (currentCell.getX() != 0 && currentCell.getY() != 0) {
+ battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY() - 1).setHit(true);
+ }
+ }
+
+}
diff --git a/src/main/java/course_project/utils/RandomCellPositionGenerator.java b/src/main/java/course_project/utils/RandomCellPositionGenerator.java
new file mode 100644
index 00000000..0a44065d
--- /dev/null
+++ b/src/main/java/course_project/utils/RandomCellPositionGenerator.java
@@ -0,0 +1,15 @@
+package course_project.utils;
+
+import java.security.SecureRandom;
+
+import static course_project.gamestuff.field.Field.FIELD_SIZE;
+
+public class RandomCellPositionGenerator {
+
+ private RandomCellPositionGenerator() {}
+
+ public static int generateRandomForCell() {
+ return new SecureRandom().nextInt(FIELD_SIZE);
+ }
+
+}
diff --git a/src/main/java/course_project/utils/RandomShipDirectionGenerator.java b/src/main/java/course_project/utils/RandomShipDirectionGenerator.java
new file mode 100644
index 00000000..bf03c2ff
--- /dev/null
+++ b/src/main/java/course_project/utils/RandomShipDirectionGenerator.java
@@ -0,0 +1,11 @@
+package course_project.utils;
+
+public class RandomShipDirectionGenerator {
+
+ private RandomShipDirectionGenerator() {}
+
+ public static boolean generateRandomDirection() {
+ return Math.random() <= 0.5;
+ }
+
+}
diff --git a/src/main/java/course_project/utils/ShipOnBattlefieldPlacer.java b/src/main/java/course_project/utils/ShipOnBattlefieldPlacer.java
new file mode 100644
index 00000000..dd23070d
--- /dev/null
+++ b/src/main/java/course_project/utils/ShipOnBattlefieldPlacer.java
@@ -0,0 +1,141 @@
+package course_project.utils;
+
+import course_project.gamestuff.field.Battlefield;
+import course_project.gamestuff.field.Cell;
+import course_project.gamestuff.ships.Ship;
+
+import static course_project.gamestuff.field.Field.FIELD_SIZE;
+
+/**
+ * Ship placing on owner battlefield
+ * checkers for availability of addition ship.
+ */
+public final class ShipOnBattlefieldPlacer {
+
+ private ShipOnBattlefieldPlacer() {}
+
+ public static boolean isEnoughSpaceToPlaceShip(Cell fromCell, int shipSize, boolean isHorizontal) {
+ return isHorizontal ?
+ fromCell.getY() >= 0 && fromCell.getY() + shipSize <= FIELD_SIZE :
+ fromCell.getX() >= 0 && fromCell.getX() + shipSize <= FIELD_SIZE;
+ }
+
+ public static boolean addColumnOfCells(Battlefield battlefield, Cell firstCell, Ship ship) {
+ if (isAbleToAddColumnOfCells(battlefield, firstCell, ship.getSize())) {
+ int y = firstCell.getY();
+ ship.setStartCell(firstCell);
+ ship.setVertical();
+ for (int i = firstCell.getX(); i < firstCell.getX() + ship.getSize(); i++) {
+ battlefield.getCellByPosition(i, y).placeShipOnCell(ship);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean addRowOfCells(Battlefield battlefield, Cell firstCell, Ship ship) {
+ if (isAbleToAddRowOfCells(battlefield, firstCell, ship.getSize())) {
+ int x = firstCell.getX();
+ ship.setStartCell(firstCell);
+ for (int j = firstCell.getY(); j < firstCell.getY() + ship.getSize(); j++) {
+ battlefield.getCellByPosition(x, j).placeShipOnCell(ship);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isAbleToAddColumnOfCells(Battlefield battlefield, Cell firstCell, int shipSize) {
+ if (!(isCellAboveFromCurrentIsEmpty(battlefield, firstCell) &&
+ isCellDiagonalLeftAboveFromCurrentIsEmpty(battlefield, firstCell) &&
+ isCellDiagonalRightAboveFromCurrentIsEmpty(battlefield, firstCell))) {
+ return false;
+ }
+ int y = firstCell.getY();
+ for (int i = firstCell.getX(); i < firstCell.getX() + shipSize; i++) {
+ Cell currentCell = battlefield.getCellByPosition(i, y);
+ if (!(battlefield.isCellEmpty(currentCell) &&
+ isCellBelowFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellLeftFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellRightFromCurrentIsEmpty(battlefield, currentCell))) {
+ return false;
+ }
+ if (i == firstCell.getX() + shipSize - 1 &&
+ !(isCellDiagonalLeftBelowFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellDiagonalRightBelowFromCurrentIsEmpty(battlefield, currentCell))
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isAbleToAddRowOfCells(Battlefield battlefield, Cell firstCell, int shipSize) {
+ if (!(isCellLeftFromCurrentIsEmpty(battlefield, firstCell) &&
+ isCellDiagonalLeftAboveFromCurrentIsEmpty(battlefield, firstCell) &&
+ isCellDiagonalLeftBelowFromCurrentIsEmpty(battlefield, firstCell) &&
+ isCellDiagonalRightAboveFromCurrentIsEmpty(battlefield, firstCell))
+ ) {
+ return false;
+ }
+ int x = firstCell.getX();
+ for (int j = firstCell.getY(); j < firstCell.getY() + shipSize; j++) {
+ Cell currentCell = battlefield.getCellByPosition(x, j);
+ if (!(battlefield.isCellEmpty(currentCell) &&
+ isCellRightFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellAboveFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellBelowFromCurrentIsEmpty(battlefield, currentCell))
+ ) {
+ return false;
+ }
+ if (j == firstCell.getY() + shipSize - 1 &&
+ !(isCellDiagonalRightAboveFromCurrentIsEmpty(battlefield, currentCell) &&
+ isCellDiagonalRightBelowFromCurrentIsEmpty(battlefield, currentCell))
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isCellRightFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getY() == FIELD_SIZE - 1 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX(), currentCell.getY() + 1));
+ }
+
+ private static boolean isCellLeftFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getY() == 0 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX(), currentCell.getY() - 1));
+ }
+
+ private static boolean isCellAboveFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == 0 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY()));
+ }
+
+ private static boolean isCellBelowFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == FIELD_SIZE - 1 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY()));
+ }
+
+ private static boolean isCellDiagonalLeftAboveFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == 0 || currentCell.getY() == 0 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY() - 1));
+ }
+
+ private static boolean isCellDiagonalRightAboveFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == 0 || currentCell.getY() == FIELD_SIZE - 1 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() - 1, currentCell.getY() + 1));
+ }
+
+ private static boolean isCellDiagonalLeftBelowFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == FIELD_SIZE - 1 || currentCell.getY() == 0 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY() - 1));
+ }
+
+ private static boolean isCellDiagonalRightBelowFromCurrentIsEmpty(Battlefield battlefield, Cell currentCell) {
+ return currentCell.getX() == FIELD_SIZE - 1 || currentCell.getY() == FIELD_SIZE - 1 ||
+ battlefield.isCellEmpty(battlefield.getCellByPosition(currentCell.getX() + 1, currentCell.getY() + 1));
+ }
+
+}
diff --git a/src/main/java/homework_1/Main.java b/src/main/java/homework_1/Main.java
index 07c029a2..123ea4ca 100644
--- a/src/main/java/homework_1/Main.java
+++ b/src/main/java/homework_1/Main.java
@@ -2,8 +2,18 @@
public class Main {
- public static void main(String[] args) {
- System.out.println("Hello homework!");
- }
+ private static final String RED = "\u001b[31m";
+ private static final String RESET = "\u001b[0m";
+
+ public static void main(String[] args) {
+ for (String arg : args) {
+ if (arg.equalsIgnoreCase("ошибка") || arg.equalsIgnoreCase("error")) {
+ System.out.println(RED + "Alarm!" + RESET);
+ break;
+ }
+
+ System.out.println(arg + ": " + arg.length() + " letters");
+ }
+ }
}
diff --git a/src/main/java/homework_2/Colors.java b/src/main/java/homework_2/Colors.java
new file mode 100644
index 00000000..78fecda2
--- /dev/null
+++ b/src/main/java/homework_2/Colors.java
@@ -0,0 +1,15 @@
+package homework_2;
+
+public final class Colors {
+
+ private Colors() {}
+
+ public static final String ANSI_RESET = "\u001B[0m";
+ public static final String ANSI_RED = "\033[1;91m";
+ public static final String ANSI_GREEN = "\033[1;92m";
+ public static final String ANSI_YELLOW = "\033[1;93m";
+ public static final String BLUE_BG = "\033[0;104m";
+ public static final String PURPLE_BG = "\033[0;105m";
+ public static final String CYAN_BG = "\033[0;106m";
+
+}
diff --git a/src/main/java/homework_2/IOMod.java b/src/main/java/homework_2/IOMod.java
new file mode 100644
index 00000000..2a43845e
--- /dev/null
+++ b/src/main/java/homework_2/IOMod.java
@@ -0,0 +1,32 @@
+package homework_2;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class IOMod {
+
+ public static final String ERROR = "Only 1 non-negative integer is allowed as passed parameter";
+ public static final String FORMAT_ERROR = "Passed parameters should match the format [positive integer] [positive integer] [even|odd]";
+
+ public static int bufferedReaderIntReader() {
+ int inp = -1;
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
+ inp = Integer.parseInt(reader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ return inp;
+ }
+ return inp;
+ }
+
+ public static String bufferedReaderStringReader() {
+ String s = "";
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
+ s = reader.readLine();
+ } catch (IOException e) {
+ System.out.println(FORMAT_ERROR);
+ }
+ return s;
+ }
+
+}
diff --git a/src/main/java/homework_2/pyramid_printer/Main.java b/src/main/java/homework_2/pyramid_printer/Main.java
new file mode 100644
index 00000000..675fbc15
--- /dev/null
+++ b/src/main/java/homework_2/pyramid_printer/Main.java
@@ -0,0 +1,11 @@
+package homework_2.pyramid_printer;
+
+public class Main {
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ new PyramidPrinter().run();
+ } else {
+ new OptimizedPyramidPrinter().run(args[0]);
+ }
+ }
+}
diff --git a/src/main/java/homework_2/pyramid_printer/OptimizedPyramidPrinter.java b/src/main/java/homework_2/pyramid_printer/OptimizedPyramidPrinter.java
new file mode 100644
index 00000000..80dfb85e
--- /dev/null
+++ b/src/main/java/homework_2/pyramid_printer/OptimizedPyramidPrinter.java
@@ -0,0 +1,38 @@
+package homework_2.pyramid_printer;
+
+import static homework_2.IOMod.*;
+import static homework_2.Colors.*;
+
+public class OptimizedPyramidPrinter {
+
+ public void run(String arg) {
+ if (arg.equalsIgnoreCase("o") || arg.equalsIgnoreCase("optimized")) {
+ System.out.print("Please, input Pyramid height: ");
+ int h = bufferedReaderIntReader();
+ if (h < 0) {
+ System.out.println(ERROR);
+ return;
+ }
+ printPyramid(h);
+ } else {
+ System.out.println("Incorrect argument. You can call this app as:\n" +
+ PURPLE_BG + "java Main -o" + ANSI_RESET + "\nor\n" +
+ BLUE_BG + "java Main -optimized" + ANSI_RESET);
+ }
+ }
+
+ private void printPyramid(int height) {
+ for (int i = 0; i < height; i++) {
+ System.out.println(genPyramidRow(i + 1));
+ }
+ }
+
+ private String genPyramidRow(int rowLen) {
+ StringBuilder xRow = new StringBuilder();
+ for (int i = 0; i < rowLen; i++) {
+ xRow.append('x');
+ }
+ return xRow.toString();
+ }
+
+}
diff --git a/src/main/java/homework_2/pyramid_printer/PyramidPrinter.java b/src/main/java/homework_2/pyramid_printer/PyramidPrinter.java
new file mode 100644
index 00000000..ce2facfc
--- /dev/null
+++ b/src/main/java/homework_2/pyramid_printer/PyramidPrinter.java
@@ -0,0 +1,27 @@
+package homework_2.pyramid_printer;
+
+import static homework_2.IOMod.ERROR;
+import static homework_2.IOMod.bufferedReaderIntReader;
+
+public class PyramidPrinter {
+
+ public void run() {
+ System.out.print("Please, input Pyramid height: ");
+ int height = bufferedReaderIntReader();
+ if (height < 0) {
+ System.out.println(ERROR);
+ return;
+ }
+ printPyramid(height);
+ }
+
+ private void printPyramid(int height) {
+ for (int i = 0; i < height; i++) {
+ for (int j = 1; j <= i + 1; j++) {
+ System.out.print("x");
+ }
+ System.out.println();
+ }
+ }
+
+}
diff --git a/src/main/java/homework_2/pyramid_printer/README.md b/src/main/java/homework_2/pyramid_printer/README.md
new file mode 100644
index 00000000..b4420c32
--- /dev/null
+++ b/src/main/java/homework_2/pyramid_printer/README.md
@@ -0,0 +1,15 @@
+### Pyramid Printer App
+
+__Task:__
+
+- read a number from the command line once
+
+- print Pyramid with this number as a height
+
+- on any error print to command line "Only 1 non-negative integer is allowed as passed parameter"
+
+- if input value = 0 prints nothing
+
+__Extra arguments:__
+- Much faster then the regular App but uses more RAM resource;
+ can be called by "java Main -o" or "java Main -optimized"
\ No newline at end of file
diff --git a/src/main/java/homework_2/random_chars_table/Main.java b/src/main/java/homework_2/random_chars_table/Main.java
new file mode 100644
index 00000000..e3879e1b
--- /dev/null
+++ b/src/main/java/homework_2/random_chars_table/Main.java
@@ -0,0 +1,9 @@
+package homework_2.random_chars_table;
+
+public class Main {
+
+ public static void main(String[] args) {
+ new RandomCharsTable().run();
+ }
+
+}
diff --git a/src/main/java/homework_2/random_chars_table/README.md b/src/main/java/homework_2/random_chars_table/README.md
new file mode 100644
index 00000000..1014f2ba
--- /dev/null
+++ b/src/main/java/homework_2/random_chars_table/README.md
@@ -0,0 +1,11 @@
+### Random Chars Table App
+
+__Task:__
+
+- read in one line 2 numbers and string - length and width of the table/2d-matrix and strategy (even/odd)
+
+- print table filled with random chars from A to Z
+
+- print in a command line in one line with commas all odd or even chars from table depended on chosen strategy
+
+- on any error should print in a command line "Passed parameters should match the format [positive integer] [positive integer] [even|odd]"
\ No newline at end of file
diff --git a/src/main/java/homework_2/random_chars_table/RandomCharsTable.java b/src/main/java/homework_2/random_chars_table/RandomCharsTable.java
new file mode 100644
index 00000000..d90d61c0
--- /dev/null
+++ b/src/main/java/homework_2/random_chars_table/RandomCharsTable.java
@@ -0,0 +1,91 @@
+package homework_2.random_chars_table;
+
+import javax.xml.bind.ValidationException;
+import java.util.Random;
+
+import static homework_2.IOMod.*;
+
+public class RandomCharsTable {
+
+ private static final int RAND_MIN_VALUE = 65; // A
+ private static final int RAND_MAX_VALUE = 90; // Z
+
+ public void run() {
+ inputData();
+ }
+
+ private void inputData() {
+ System.out.print("Input table length, table width, table strategy(even/odd): ");
+ try {
+ String s = bufferedReaderStringReader();
+ String[] parameters = s.split(" ");
+ if (!isInputValid(parameters)) {
+ throw new ValidationException("");
+ }
+ int tableLength = Integer.parseInt(parameters[0]);
+ int tableWidth = Integer.parseInt(parameters[1]);
+ boolean strategy = parameters[2].equalsIgnoreCase("even");
+
+ printRandomAndGenerateResult(tableLength, tableWidth, strategy);
+ } catch (NullPointerException | ValidationException e) {
+ System.out.println(FORMAT_ERROR);
+ }
+ }
+
+ private boolean isInputValid(String[] parameters) {
+ if (parameters.length == 3) {
+ return parameters[0].chars().allMatch(Character::isDigit) &&
+ !parameters[0].equals("0") &&
+ parameters[1].chars().allMatch(Character::isDigit) &&
+ !parameters[1].equals("0") &&
+ (parameters[2].equalsIgnoreCase("odd") || parameters[2].equalsIgnoreCase("even"));
+ }
+ return false;
+ }
+
+ private void printRandomAndGenerateResult(int length, int width, boolean strategy) {
+ Character[][] randAbc = new Character[length][width];
+ Random random = new Random();
+ StringBuilder result = new StringBuilder();
+
+ for (int i = 0; i < length; i++) {
+ System.out.print("| ");
+ for (int j = 0; j < width; j++) {
+ randAbc[i][j] = (char) (random.nextInt((RAND_MAX_VALUE - RAND_MIN_VALUE) + 1) + RAND_MIN_VALUE);
+ System.out.print(randAbc[i][j] + " | ");
+
+ // result generating on-the-go
+ generateResult(strategy ? 0 : 1, randAbc[i][j], result);
+ }
+ System.out.println();
+ }
+ printResult(strategy, result);
+ }
+
+ private void generateResult(int strategy, char ch, StringBuilder result) {
+ if ((ch % 2 == strategy) && (!String.valueOf(result).contains(String.valueOf(ch)))) {
+ result.append(ch);
+ }
+ }
+
+ private void printResult(boolean strategy, StringBuilder result) {
+ if (result.length() == 0) {
+ return;
+ }
+ if (strategy) {
+ System.out.print("Even letters - ");
+ } else {
+ System.out.print("Odd letters - ");
+ }
+ int i = 0;
+ while (true) {
+ if (i == result.length() - 1) {
+ System.out.print(result.charAt(i));
+ break;
+ }
+ System.out.print(result.charAt(i) + ", ");
+ i++;
+ }
+ }
+
+}
diff --git a/src/main/java/homework_2/traffic_light/ExtraTrafficLight.java b/src/main/java/homework_2/traffic_light/ExtraTrafficLight.java
new file mode 100644
index 00000000..1c9d64d4
--- /dev/null
+++ b/src/main/java/homework_2/traffic_light/ExtraTrafficLight.java
@@ -0,0 +1,42 @@
+package homework_2.traffic_light;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import static homework_2.Colors.*;
+
+public class ExtraTrafficLight {
+
+ public void run(String arg) {
+ if (arg.equalsIgnoreCase("now")) {
+ int seconds = getTime();
+ colorPick(seconds%60);
+ } else {
+ System.out.println("Incorrect argument. You can call this app as:\n" +
+ PURPLE_BG + "java Main -now" + ANSI_RESET);
+ }
+ }
+
+ private int getTime() {
+ LocalDateTime now = LocalDateTime.now();
+ String nowStr = now.format(DateTimeFormatter.ofPattern("hh:mm:ss a"));
+ System.out.println("Current time is: " + nowStr);
+ return now.toLocalTime().toSecondOfDay();
+ }
+
+ private void colorPick(int secs) {
+ // 0 <= light < 35 green, 35 <= light < 40 yellow, 40 <= light < 55 red, 55 <= light < 60 yellow
+ if (secs >= 0 && secs < 35) {
+ lightPrint(ANSI_GREEN, "GREEN");
+ } else if ((secs >= 35 && secs < 40) || (secs >= 55 && secs < 60)) {
+ lightPrint(ANSI_YELLOW, "YELLOW");
+ } else {
+ lightPrint(ANSI_RED, "RED");
+ }
+ }
+
+ private void lightPrint(String consoleColor, String lightColor) {
+ System.out.println(consoleColor + lightColor + ANSI_RESET);
+ }
+
+}
diff --git a/src/main/java/homework_2/traffic_light/Main.java b/src/main/java/homework_2/traffic_light/Main.java
new file mode 100644
index 00000000..4dc659df
--- /dev/null
+++ b/src/main/java/homework_2/traffic_light/Main.java
@@ -0,0 +1,19 @@
+package homework_2.traffic_light;
+
+/*
+Traffic Lights App
+
+you can use 'now' argument to get result for the current time ;)
+java Main -now
+
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ new TrafficLight().run();
+ } else {
+ new ExtraTrafficLight().run(args[0]);
+ }
+ }
+}
diff --git a/src/main/java/homework_2/traffic_light/README.md b/src/main/java/homework_2/traffic_light/README.md
new file mode 100644
index 00000000..83a2c387
--- /dev/null
+++ b/src/main/java/homework_2/traffic_light/README.md
@@ -0,0 +1,23 @@
+### Traffic Light App
+
+__Task:__
+
+- read input how much time gone from the day start from the command line,
+parse to Integer, print to the command line traffic light color (GREEN, YELLOW, RED - with different colors)
+
+- traffic lights cycle: 60 seconds:
+ + 0 <= light < 35 - green
+ + 35 <= light < 40 - yellow
+ + 40 <= light < 55 - red
+ + 55 <= light < 60 - yellow
+
+- input limitations:
+ + minimal value is 0
+ + maximal value is 24*60*60 - 1 = 86399
+
+- if over then 86399 should print "The day is over"
+
+- if input value is incorrect should print "Only 1 non-negative integer is allowed as passed parameter"
+
+__Extra arguments:__
+- App will count the traffic lights result for the current time if call "java Main -now"
\ No newline at end of file
diff --git a/src/main/java/homework_2/traffic_light/TrafficLight.java b/src/main/java/homework_2/traffic_light/TrafficLight.java
new file mode 100644
index 00000000..46740556
--- /dev/null
+++ b/src/main/java/homework_2/traffic_light/TrafficLight.java
@@ -0,0 +1,45 @@
+package homework_2.traffic_light;
+
+import homework_2.IOMod;
+
+import static homework_2.IOMod.*;
+import static homework_2.Colors.*;
+
+public class TrafficLight {
+
+ public void run() {
+ inputSeconds();
+ }
+
+ private void inputSeconds() {
+ System.out.print("Please, input seconds gone from the day start: ");
+ try {
+ int secondsGone = IOMod.bufferedReaderIntReader();
+ if (secondsGone < 0) {
+ System.out.println(ERROR);
+ } else if (secondsGone >= 86400) {
+ System.out.println("The day is over");
+ } else {
+ colorPick(secondsGone % 60);
+ }
+ } catch (NumberFormatException exc) {
+ System.out.println(ERROR);
+ }
+ }
+
+ private void colorPick(int secs) {
+ // 0 <= light < 35 green, 35 <= light < 40 yellow, 40 <= light < 55 red, 55 <= light < 60 yellow
+ if (secs < 35) {
+ lightPrint(ANSI_GREEN, "GREEN");
+ } else if ((secs < 40) || (secs >= 55 && secs < 60)) {
+ lightPrint(ANSI_YELLOW, "YELLOW");
+ } else {
+ lightPrint(ANSI_RED, "RED");
+ }
+ }
+
+ private void lightPrint(String consoleColor, String lightColor) {
+ System.out.println(consoleColor + lightColor + ANSI_RESET);
+ }
+
+}
diff --git a/src/main/java/homework_3/ImmutableClass.java b/src/main/java/homework_3/ImmutableClass.java
new file mode 100644
index 00000000..59cd2e2a
--- /dev/null
+++ b/src/main/java/homework_3/ImmutableClass.java
@@ -0,0 +1,66 @@
+package homework_3;
+
+/*
+Immutable class
+ The class must be declared as final (So that child classes can’t be created)
+ Data members in the class must be declared as private (So that direct access is not allowed)
+ Data members in the class must be declared as final (So that we can’t change the value of it after object creation)
+ A parameterized constructor should initialize all the fields performing a deep copy (So that data members can’t be modified with object reference)
+ Deep Copy of objects should be performed in the getter methods (To return a copy rather than returning the actual object reference)
+ No setters (To not have the option to change the value of the instance variable)
+ */
+
+import java.util.LinkedList;
+import java.util.List;
+
+public final class ImmutableClass {
+
+ private final List allDataList; // mutable type
+ private final int age; // primitive type
+ private final double weight; // primitive type
+
+ public ImmutableClass() {
+ this.age = 0;
+ this.weight = 0.0;
+ this.allDataList = new LinkedList<>();
+ initAllDataList();
+ }
+
+ public ImmutableClass(int age, double weight) {
+ this.age = age;
+ this.weight = weight;
+ this.allDataList = new LinkedList<>();
+ initAllDataList();
+ }
+
+ public ImmutableClass(List allData, int age, double weight) {
+ this.age = age;
+ this.weight = weight;
+ this.allDataList = new LinkedList<>(allData);
+ initAllDataList();
+ }
+
+ private void initAllDataList() {
+ this.allDataList.add(age);
+ this.allDataList.add(weight);
+ }
+
+ public List getAllDataList() {
+ return new LinkedList<>(this.allDataList);
+ }
+
+ public ImmutableClass getNewObjectWithChanges(List changes) {
+ List list = getAllDataList();
+ list.addAll(changes);
+ return new ImmutableClass(list, this.age, this.weight);
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public double getWeight() {
+ return weight;
+ }
+
+}
diff --git a/src/main/java/homework_3/Main.java b/src/main/java/homework_3/Main.java
new file mode 100644
index 00000000..9884add3
--- /dev/null
+++ b/src/main/java/homework_3/Main.java
@@ -0,0 +1,19 @@
+package homework_3;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Main {
+
+ public static void main(String[] args) {
+ ImmutableClass obj = new ImmutableClass(2, 3.0);
+ obj.getAllDataList().forEach(System.out::println);
+ List changes = new LinkedList<>();
+ changes.add(1);
+ changes.add(12.);
+ changes.add(13);
+ ImmutableClass obj2 = obj.getNewObjectWithChanges(changes);
+ obj2.getAllDataList().forEach(System.out::println);
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_annotation/ResourcesPath.java b/src/main/java/homework_4/custom_annotation/ResourcesPath.java
new file mode 100644
index 00000000..89c7a5ce
--- /dev/null
+++ b/src/main/java/homework_4/custom_annotation/ResourcesPath.java
@@ -0,0 +1,12 @@
+package homework_4.custom_annotation;
+
+import java.lang.annotation.*;
+
+
+@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ResourcesPath {
+
+ String dirPath() default "./src/main/resources/custom_file_reader/";
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/CustomFileReader.java b/src/main/java/homework_4/custom_file_reader/CustomFileReader.java
new file mode 100644
index 00000000..6a86ca65
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/CustomFileReader.java
@@ -0,0 +1,67 @@
+package homework_4.custom_file_reader;
+
+import homework_4.custom_annotation.ResourcesPath;
+import homework_4.custom_file_reader.utils.*;
+
+import static homework_4.custom_file_reader.utils.ModifiedStringPrinter.printModifiedString;
+
+
+public class CustomFileReader {
+
+ private final String fileName;
+ private final String dirPath;
+
+ @ResourcesPath
+ public CustomFileReader() {
+ this.fileName = "file.txt";
+ String tmpDirPath;
+ try {
+ ResourcesPath resourcesPath = (ResourcesPath) this.getClass().getConstructor().getDeclaredAnnotations()[0];
+ tmpDirPath = resourcesPath.dirPath();
+ } catch (NoSuchMethodException e) {
+ tmpDirPath = "./src/main/resources/"; // not including custom folder!
+ }
+ this.dirPath = tmpDirPath;
+ }
+
+ public CustomFileReader(String dirPath, String fileName) {
+ this.dirPath = dirPath;
+ this.fileName = fileName;
+ }
+
+ // using scanner
+ public synchronized void run1() {
+ doExcellent(new ScannerFileRead().fileReader(this.dirPath, this.fileName));
+ }
+
+ // using buffered reader
+ public synchronized void run2() {
+ doExcellent(new BufferedReaderAsStreamFileRead().fileReader(this.dirPath, this.fileName));
+ }
+
+ // using nio
+ public synchronized void run3() {
+ doExcellent(new NIOFileRead().fileReader(this.dirPath, this.fileName));
+ }
+
+ // using InputStreamReader
+ public synchronized void run4() {
+ doExcellent(new InputStreamReaderFileRead().fileReader(this.dirPath, this.fileName));
+ }
+
+ // prints all files and subdirectories from set directory
+ public void showDir() {
+ DirectoryScan.scanDir(this.dirPath);
+ }
+
+ private boolean isNotExists(String res) {
+ return res == null;
+ }
+
+ private void doExcellent(String result) {
+ if (isNotExists(result)) {
+ System.out.println("File " + this.fileName + " not found at: " + this.dirPath);
+ } else printModifiedString(result);
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/BufferedReaderAsStreamFileRead.java b/src/main/java/homework_4/custom_file_reader/utils/BufferedReaderAsStreamFileRead.java
new file mode 100644
index 00000000..4783f3f2
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/BufferedReaderAsStreamFileRead.java
@@ -0,0 +1,22 @@
+package homework_4.custom_file_reader.utils;
+
+import java.io.*;
+
+
+public class BufferedReaderAsStreamFileRead implements FileReadability {
+
+ @Override
+ public String fileReader(String dirPath, String fileName) {
+ try (final FileInputStream fileInputStream = new FileInputStream(dirPath + fileName);
+ final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream))) {
+ final StringBuilder stringBuilder = new StringBuilder(fileInputStream.available());
+ while (bufferedReader.ready()) {
+ stringBuilder.append(bufferedReader.readLine()).append("\n");
+ }
+ return String.valueOf(stringBuilder);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/DirectoryScan.java b/src/main/java/homework_4/custom_file_reader/utils/DirectoryScan.java
new file mode 100644
index 00000000..ec4bf265
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/DirectoryScan.java
@@ -0,0 +1,37 @@
+package homework_4.custom_file_reader.utils;
+
+import java.io.File;
+
+
+public class DirectoryScan {
+
+ private DirectoryScan() { }
+
+ public static void scanDir(String dirPath) {
+ File[] filesList = new File(dirPath).listFiles();
+ if (filesList != null && filesList.length >= 1) {
+ System.out.println(dirPath + " contains:" + "\n--- --- ---");
+ for (File file : filesList) {
+ System.out.println(file.getName() + "\tat: " + file.getAbsolutePath());
+ System.out.println("---");
+ }
+ System.out.println("--- --- ---");
+ } else System.out.println(dirPath + " is empty!");
+ }
+
+ // additional method for extra usage
+ public static boolean isInDir(String dirPath, String fileName) {
+ File[] filesList = new File(dirPath).listFiles();
+ if (filesList == null) {
+ System.out.println(dirPath + " is empty!");
+ return false;
+ }
+ for (File file : filesList) {
+ if (file.getName().equalsIgnoreCase(fileName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/FileReadability.java b/src/main/java/homework_4/custom_file_reader/utils/FileReadability.java
new file mode 100644
index 00000000..b2e8c1f0
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/FileReadability.java
@@ -0,0 +1,8 @@
+package homework_4.custom_file_reader.utils;
+
+
+public interface FileReadability {
+
+ String fileReader(String dirPath, String fileName);
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/InputStreamReaderFileRead.java b/src/main/java/homework_4/custom_file_reader/utils/InputStreamReaderFileRead.java
new file mode 100644
index 00000000..e7f53b8c
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/InputStreamReaderFileRead.java
@@ -0,0 +1,27 @@
+package homework_4.custom_file_reader.utils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+
+public class InputStreamReaderFileRead implements FileReadability {
+
+ @Override
+ public String fileReader(String dirPath, String fileName) {
+ try (FileInputStream inputStream = new FileInputStream(dirPath + fileName);
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
+ StringBuilder lines = new StringBuilder();
+ String line;
+ while (null != (line = bufferedReader.readLine())) {
+ lines.append(line).append("\n");
+ }
+ return String.valueOf(lines);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/ModifiedStringPrinter.java b/src/main/java/homework_4/custom_file_reader/utils/ModifiedStringPrinter.java
new file mode 100644
index 00000000..021de505
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/ModifiedStringPrinter.java
@@ -0,0 +1,13 @@
+package homework_4.custom_file_reader.utils;
+
+
+public class ModifiedStringPrinter {
+
+ private ModifiedStringPrinter() { }
+
+ // printing string without '.' and ',' symbols
+ public static void printModifiedString(String str) {
+ System.out.println(str.replaceAll("[,.]", ""));
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/NIOFileRead.java b/src/main/java/homework_4/custom_file_reader/utils/NIOFileRead.java
new file mode 100644
index 00000000..e792a9cf
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/NIOFileRead.java
@@ -0,0 +1,26 @@
+package homework_4.custom_file_reader.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+
+
+public class NIOFileRead implements FileReadability {
+
+ @Override
+ public String fileReader(String dirPath, String fileName) {
+ File textFile;
+ try {
+ textFile = new File(dirPath + fileName);
+ List lines;
+ lines = Files.readAllLines(textFile.toPath());
+ StringBuilder stringBuilder = new StringBuilder();
+ lines.forEach(line -> stringBuilder.append(line).append("\n"));
+ return String.valueOf(stringBuilder);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/homework_4/custom_file_reader/utils/ScannerFileRead.java b/src/main/java/homework_4/custom_file_reader/utils/ScannerFileRead.java
new file mode 100644
index 00000000..f0137449
--- /dev/null
+++ b/src/main/java/homework_4/custom_file_reader/utils/ScannerFileRead.java
@@ -0,0 +1,24 @@
+package homework_4.custom_file_reader.utils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+
+
+public class ScannerFileRead implements FileReadability {
+
+ @Override
+ public String fileReader(String dirPath, String fileName) {
+ try (Scanner scanner = new Scanner(new File(dirPath + fileName))) {
+ StringBuilder stringBuilder = new StringBuilder();
+ while (scanner.hasNextLine()) {
+ stringBuilder.append(scanner.nextLine());
+ stringBuilder.append("\n");
+ }
+ return String.valueOf(stringBuilder);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/homework_4/singleton/Main.java b/src/main/java/homework_4/singleton/Main.java
new file mode 100644
index 00000000..739623ee
--- /dev/null
+++ b/src/main/java/homework_4/singleton/Main.java
@@ -0,0 +1,15 @@
+package homework_4.singleton;
+
+/*
+ Singleton provides access to CustomFileReader methods through GUI.
+ It's the only one instance of GUI able to run (thread-safe).
+ */
+
+
+public class Main {
+
+ public static void main(String[] args) {
+ SingletonMenu.getInstance();
+ }
+
+}
diff --git a/src/main/java/homework_4/singleton/SingletonMenu.java b/src/main/java/homework_4/singleton/SingletonMenu.java
new file mode 100644
index 00000000..0e4a3c6e
--- /dev/null
+++ b/src/main/java/homework_4/singleton/SingletonMenu.java
@@ -0,0 +1,79 @@
+package homework_4.singleton;
+
+import homework_4.custom_file_reader.CustomFileReader;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+
+// Bill Pugh Singleton Implementation
+public class SingletonMenu {
+
+ private SingletonMenu() {
+ guiGenerator();
+ }
+
+ private static class SingletonHelper {
+ private static final SingletonMenu INSTANCE = new SingletonMenu();
+ }
+
+ public static SingletonMenu getInstance() {
+ return SingletonHelper.INSTANCE;
+ }
+
+ private void guiGenerator() {
+ JFrame frame = new JFrame("Singleton Menu for CustomFileReader");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setSize(500, 350);
+
+ JPanel mainPanel = new JPanel();
+ JButton button1 = new JButton("read file using Scanner");
+ JButton button2 = new JButton("read file using BufferedReader");
+ JButton button3 = new JButton("read file using NIO");
+ JButton button4 = new JButton("read file using InputStreamReader");
+ JButton button5 = new JButton("show files in directory");
+ mainPanel.add(button1);
+ mainPanel.add(button2);
+ mainPanel.add(button3);
+ mainPanel.add(button4);
+ mainPanel.add(button5);
+
+ CustomFileReader customFileReader = new CustomFileReader();
+ addButtonListener(button1, customFileReader, 1);
+ addButtonListener(button2, customFileReader, 2);
+ addButtonListener(button3, customFileReader, 3);
+ addButtonListener(button4, customFileReader, 4);
+ addButtonListener(button5, customFileReader, 10);
+
+ // Adding Components to the frame
+ frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
+ frame.setVisible(true);
+ }
+
+ private void addButtonListener(@NotNull JButton button, CustomFileReader customFileReader, int methodNumber) {
+ button.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ buttonActionPerformed();
+ }
+
+ private void buttonActionPerformed() {
+ if (methodNumber == 1) {
+ customFileReader.run1();
+ } else if (methodNumber == 2) {
+ customFileReader.run2();
+ } else if (methodNumber == 3) {
+ customFileReader.run3();
+ } else if (methodNumber == 4) {
+ customFileReader.run4();
+ } else if (methodNumber == 10) {
+ customFileReader.showDir();
+ } else throw new IllegalArgumentException();
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/homework_5/custom_regex_matcher/CustomRegexMatcher.java b/src/main/java/homework_5/custom_regex_matcher/CustomRegexMatcher.java
new file mode 100644
index 00000000..4ddadbac
--- /dev/null
+++ b/src/main/java/homework_5/custom_regex_matcher/CustomRegexMatcher.java
@@ -0,0 +1,27 @@
+package homework_5.custom_regex_matcher;
+
+import java.util.Scanner;
+
+public class CustomRegexMatcher {
+
+ private CustomRegexMatcher() { }
+
+ private static final String PHONE_NUMBER_FORMAT = "(\\+\\d)\\s\\(\\d{3}\\)\\s(\\d{3})-(\\d{4})";
+
+ public static void run() {
+ System.out.println(validate(getInput()));
+ }
+
+ private static String getInput() {
+ try (Scanner scanner = new Scanner(System.in)) {
+ return scanner.nextLine().trim();
+ }
+ }
+
+ // should be written as: `+# (###) ###-####`, where # - is an integer number
+ private static Boolean validate(String str) {
+ return str.matches(PHONE_NUMBER_FORMAT);
+ }
+
+
+}
diff --git a/src/main/java/homework_5/power_of_number/PowerOfNumber.java b/src/main/java/homework_5/power_of_number/PowerOfNumber.java
new file mode 100644
index 00000000..701a87ac
--- /dev/null
+++ b/src/main/java/homework_5/power_of_number/PowerOfNumber.java
@@ -0,0 +1,48 @@
+package homework_5.power_of_number;
+
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+public class PowerOfNumber {
+
+ private static final String ERROR = "Only 2 non-negative integers are allowed";
+
+ public void run() {
+ int[] array = getInput();
+ if (array.length == 2) System.out.println(power(array[0], array[1]));
+ }
+
+ private int[] getInput() {
+ try (Scanner scanner = new Scanner(System.in)) {
+ System.out.println("Please, input the values in format: number power");
+ String[] input = scanner.nextLine().split(" ");
+ if (input.length == 2) {
+ int[] result = new int[2];
+ for (int i = 0; i < 2; i++) {
+ result[i] = Integer.parseInt(input[i]);
+ if (!isValid(result[i])) {
+ throw new NumberFormatException();
+ }
+ }
+ return result;
+ } else {
+ throw new NumberFormatException();
+ }
+ } catch (NumberFormatException | NoSuchElementException e) {
+ System.out.println(ERROR);
+ return new int[0];
+ }
+ }
+
+ private boolean isValid(int num) {
+ return num >= 0;
+ }
+
+ private int power(int base, int p) {
+ if (p > 0) return base * power(base, p-1);
+ if (p < 0) return 1/base * power(base, p+1);
+ return 1;
+ }
+
+
+}
diff --git a/src/main/java/homework_6/map_problems_collision_generator/Main.java b/src/main/java/homework_6/map_problems_collision_generator/Main.java
new file mode 100644
index 00000000..18a51a37
--- /dev/null
+++ b/src/main/java/homework_6/map_problems_collision_generator/Main.java
@@ -0,0 +1,20 @@
+package homework_6.map_problems_collision_generator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Main {
+
+ public static void main(String[] args) {
+
+ Map unluckyMap = new HashMap<>();
+
+ MapProblemsMutableGenerator mapProblemsMutableGenerator = new MapProblemsMutableGenerator();
+ mapProblemsMutableGenerator.setVal("ложим!");
+ unluckyMap.put(mapProblemsMutableGenerator, mapProblemsMutableGenerator.getVal());
+ mapProblemsMutableGenerator.setVal("кладем!");
+
+ System.out.println("не можИм! " + unluckyMap.get(mapProblemsMutableGenerator));
+ }
+
+}
diff --git a/src/main/java/homework_6/map_problems_collision_generator/MapProblemsCollisionGenerator.java b/src/main/java/homework_6/map_problems_collision_generator/MapProblemsCollisionGenerator.java
new file mode 100644
index 00000000..6fc72057
--- /dev/null
+++ b/src/main/java/homework_6/map_problems_collision_generator/MapProblemsCollisionGenerator.java
@@ -0,0 +1,32 @@
+package homework_6.map_problems_collision_generator;
+
+public class MapProblemsCollisionGenerator {
+
+ private final String val;
+
+ public MapProblemsCollisionGenerator() {
+ this.val = "кладем?";
+ }
+
+ public MapProblemsCollisionGenerator(String s) {
+ this.val = s;
+ }
+
+ public String getVal() {
+ return val;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || obj.getClass() != this.getClass()) return false;
+ MapProblemsCollisionGenerator that = (MapProblemsCollisionGenerator) obj;
+ return this.getVal().equals(that.getVal());
+ }
+
+}
diff --git a/src/main/java/homework_6/map_problems_collision_generator/MapProblemsMutableGenerator.java b/src/main/java/homework_6/map_problems_collision_generator/MapProblemsMutableGenerator.java
new file mode 100644
index 00000000..43486379
--- /dev/null
+++ b/src/main/java/homework_6/map_problems_collision_generator/MapProblemsMutableGenerator.java
@@ -0,0 +1,34 @@
+package homework_6.map_problems_collision_generator;
+
+import java.util.Objects;
+
+public class MapProblemsMutableGenerator {
+
+ private String val;
+
+ public MapProblemsMutableGenerator() {
+ setVal("кладем?");
+ }
+
+ public String getVal() {
+ return val;
+ }
+
+ public void setVal(String val) {
+ this.val = val;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(val);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MapProblemsMutableGenerator that = (MapProblemsMutableGenerator) o;
+ return Objects.equals(val, that.val);
+ }
+
+}
diff --git a/src/main/java/homework_7/kitten_to_cat/Cat.java b/src/main/java/homework_7/kitten_to_cat/Cat.java
new file mode 100644
index 00000000..436b45e8
--- /dev/null
+++ b/src/main/java/homework_7/kitten_to_cat/Cat.java
@@ -0,0 +1,56 @@
+package homework_7.kitten_to_cat;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+public class Cat {
+
+ private final int age;
+ private final String name;
+
+ public Cat(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ private boolean isNameValid() {
+ return this.name.length() > 2;
+ }
+
+ @Override
+ public int hashCode() {
+ if (isNameValid()) {
+ return (this.age + this.name.charAt(0)) * 2 << 5;
+ } else {
+ try {
+ Random r = SecureRandom.getInstanceStrong();
+ return r.nextInt();
+ } catch (Exception e) {
+ throw new NumberFormatException("ERROR: SecureRandom @hashCode works incorrect for " + this.getClass());
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || o.getClass() != this.getClass()) return false;
+ if (this == o) return true;
+ Cat that = (Cat) o;
+ return this.getAge() == that.getAge() && this.getName().equals(that.getName());
+ }
+
+ @Override
+ public String toString() {
+ return "Cat's name: " + this.getName() +
+ "\nCat's age: " + this.getAge();
+ }
+
+}
diff --git a/src/main/java/homework_7/kitten_to_cat/Kitten.java b/src/main/java/homework_7/kitten_to_cat/Kitten.java
new file mode 100644
index 00000000..4f2da7db
--- /dev/null
+++ b/src/main/java/homework_7/kitten_to_cat/Kitten.java
@@ -0,0 +1,63 @@
+package homework_7.kitten_to_cat;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+public class Kitten {
+
+ private final int age;
+ private final String name;
+ private final String breed;
+
+ public int getAge() {
+ return age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getBreed() {
+ return breed;
+ }
+
+ public Kitten(String name, int age, String breed) {
+ this.name = name;
+ this.age = age;
+ this.breed = breed;
+ }
+
+ private boolean isNameValid() {
+ return this.name.length() > 2;
+ }
+
+ @Override
+ public int hashCode() {
+ if (isNameValid()) {
+ return (this.age + this.name.charAt(0)) * 2 << 5;
+ } else {
+ try {
+ Random r = SecureRandom.getInstanceStrong();
+ return r.nextInt();
+ } catch (Exception e) {
+ throw new NumberFormatException("ERROR: SecureRandom @hashCode works incorrect for " + this.getClass());
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || o.getClass() != this.getClass()) return false;
+ if (this == o) return true;
+ Kitten that = (Kitten) o;
+ return this.age == that.age && this.name.equals(that.name);
+ }
+
+ @Override
+ public String toString() {
+ return "Kitten's name: " + this.getName() +
+ "\nKitten's age: " + this.getAge() +
+ "\nKitten's breed: " + this.getBreed();
+ }
+
+}
diff --git a/src/main/java/homework_7/kitten_to_cat/KittenToCatFunction.java b/src/main/java/homework_7/kitten_to_cat/KittenToCatFunction.java
new file mode 100644
index 00000000..7ba4af51
--- /dev/null
+++ b/src/main/java/homework_7/kitten_to_cat/KittenToCatFunction.java
@@ -0,0 +1,8 @@
+package homework_7.kitten_to_cat;
+
+@FunctionalInterface
+public interface KittenToCatFunction {
+
+ Cat grow(Kitten k);
+
+}
diff --git a/src/main/resources/custom_file_reader/empty_file.txt b/src/main/resources/custom_file_reader/empty_file.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/src/main/resources/custom_file_reader/file.txt b/src/main/resources/custom_file_reader/file.txt
new file mode 100644
index 00000000..d46a8616
--- /dev/null
+++ b/src/main/resources/custom_file_reader/file.txt
@@ -0,0 +1,18 @@
+“знаешь, если искать врага – обретаешь его в любом…”
+
+знаешь, если искать врага – обретаешь его в любом.
+вот, пожалуй, спроси меня – мне никто не страшен:
+я спокоен и прям и знаю, что впереди.
+я хожу без страховки с факелом надо лбом
+по стальной струне, натянутой между башен,
+когда снизу кричат только: «упади».
+разве они знают, чего мне стоило ремесло.
+разве они видели, сколько раз я орал и плакал.
+разве ступят на ветер, нащупав его изгиб.
+они думают, я дурак, которому повезло.
+если я отвечу им, я не удержу над бровями факел.
+если я отвечу им, я погиб.
+
+23 июня 2013. Ришикеш
+
+Вера Николаевна Полозкова. “Работа горя”.
\ No newline at end of file
diff --git a/src/main/resources/sea_battle/rules.txt b/src/main/resources/sea_battle/rules.txt
new file mode 100644
index 00000000..d87a56b4
--- /dev/null
+++ b/src/main/resources/sea_battle/rules.txt
@@ -0,0 +1,54 @@
+-Правила размещения кораблей (флота)
+
+Игровое поле — обычно квадрат 10×10 у каждого игрока, на котором размещается флот кораблей.
+Вертикали обычно нумеруются сверху вниз, а горизонтали помечаются буквами слева направо.
+При этом используются буквы русского алфавита от «а» до «к» (буквы «ё» и «й» обычно пропускаются)
+либо от «а» до «и» (с использованием буквы «ё»), либо буквы латинского алфавита от «a» до «j».
+
+Размещаются:
+ 1 корабль — ряд из 4 клеток («четырёхпалубный»; линкор)
+ 2 корабля — ряд из 3 клеток («трёхпалубные»; крейсера)
+ 3 корабля — ряд из 2 клеток («двухпалубные»; эсминцы)
+ 4 корабля — 1 клетка («однопалубные»; торпедные катера)
+
+При размещении корабли не могут касаться друг друга сторонами и углами.
+Встречаются, однако, варианты, когда касание углами не запрещается.
+Встречаются также варианты игры, когда корабли могут размещаться буквой «Г» («трех-» и «четырехпалубные»),
+квадратом или зигзагом («четырехпалубные»).
+Кроме того, есть варианты с другим набором кораблей (напр., один пятипалубный, два четырёхпалубных и т. д.)
+и/или другой формой поля (15×15 для пятипалубных (авианосец)).
+
+Рядом со «своим» полем чертится «чужое» такого же размера, только пустое.
+Это участок моря, где плавают корабли противника.
+
+При попадании в корабль противника — на чужом поле ставится крестик, при холостом выстреле — точка.
+Попавший стреляет ещё раз.
+
+Самыми уязвимыми являются линкор и торпедный катер: первый из-за крупных размеров,
+в связи с чем его сравнительно легко найти, а второй из-за того, что топится с одного удара,
+хотя его найти достаточно сложно.
+
+-Потопление кораблей противника
+
+Перед началом боевых действий игроки бросают жребий или договариваются, кто будет ходить первым.
+
+Игрок, выполняющий ход, совершает выстрел — называет вслух координаты клетки, в которой, по его мнению,
+находится корабль противника, например, «В1».
+
+ Если выстрел пришёлся в клетку, не занятую ни одним кораблём противника,
+ то следует ответ «Мимо!» и стрелявший игрок ставит на чужом квадрате в этом месте точку.
+ Право хода переходит к сопернику.
+
+ Если выстрел пришёлся в клетку, где находится многопалубный корабль (размером больше чем 1 клетка),
+ то следует ответ «Ранил(а)!» или «Попал(а)!», кроме одного случая.
+ Стрелявший игрок ставит на чужом поле в эту клетку крестик,
+ а его противник ставит крестик на своём поле также в эту клетку.
+ Стрелявший игрок получает право на ещё один выстрел.
+
+ Если выстрел пришёлся в клетку, где находится однотрубный корабль,
+ или последнюю непоражённую клетку многопалубного корабля,
+ то следует ответ «Убил(а)!» или «Потопил(а)!».
+ Оба игрока отмечают потопленный корабль на листе. Стрелявший игрок получает право на ещё один выстрел.
+
+
+Победителем считается тот, кто первым потопит все 10 кораблей противника.
diff --git a/src/test/java/homework_1/MainTest.java b/src/test/java/homework_1/MainTest.java
new file mode 100644
index 00000000..67de6860
--- /dev/null
+++ b/src/test/java/homework_1/MainTest.java
@@ -0,0 +1,49 @@
+package homework_1;
+
+import base.UnitBase;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class MainTest extends UnitBase {
+
+ @Test
+ void test1() {
+ String[] args = {
+ "abcdefg",
+ "hello",
+ "zero",
+ "",
+ "абвгдеёжзийклмнопрст",
+ "quit",
+ "eRrOr",
+ "ошибка"
+ };
+
+ Main.main(args);
+ printOut();
+ assertEquals("abcdefg: 7 letters", getOutputLines()[0]);
+ assertEquals("hello: 5 letters", getOutputLines()[1]);
+ assertEquals("zero: 4 letters", getOutputLines()[2]);
+ assertEquals(": 0 letters", getOutputLines()[3]);
+ assertEquals("абвгдеёжзийклмнопрст: 20 letters", getOutputLines()[4]);
+ assertEquals("quit: 4 letters", getOutputLines()[5]);
+ assertEquals("\u001b[31mAlarm!\u001b[0m", getOutputLines()[6]);
+ }
+
+ @Test
+ void test2() {
+ String[] args = {
+ "строка",
+ "0шибка",
+ "ошибкА",
+ };
+
+ Main.main(args);
+ printOut();
+ assertEquals("строка: 6 letters", getOutputLines()[0]);
+ assertEquals("0шибка: 6 letters", getOutputLines()[1]);
+ assertEquals("\u001b[31mAlarm!\u001b[0m", getOutputLines()[2]);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/homework_2/pyramid_printer/PyramidPrinterTest.java b/src/test/java/homework_2/pyramid_printer/PyramidPrinterTest.java
new file mode 100644
index 00000000..6d9fa656
--- /dev/null
+++ b/src/test/java/homework_2/pyramid_printer/PyramidPrinterTest.java
@@ -0,0 +1,73 @@
+package homework_2.pyramid_printer;
+
+import base.UnitBase;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class PyramidPrinterTest extends UnitBase {
+
+ PyramidPrinter pyramidPrinter = new PyramidPrinter();
+
+ // testing valid input
+ private static Stream validCases() {
+ return Stream.of(
+ Arguments.of(7),
+ Arguments.of(4),
+ Arguments.of(33),
+ Arguments.of(1),
+ Arguments.of(188)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("validCases")
+ void validTest(int height) {
+ setInput(String.valueOf(height));
+ pyramidPrinter.run();
+ printOut();
+ removeFromOutput("Please, input Pyramid height: ");
+ for (int i = 0; i < height; i++) {
+ StringBuilder expectedStr = new StringBuilder();
+ for (int j = 0; j < i + 1; j++) {
+ expectedStr.append("x");
+ }
+ assertEquals(String.valueOf(expectedStr), getOutputLines()[i]);
+ }
+ }
+
+ // testing not valid input values
+ private static Stream notValidCases() {
+ return Stream.of(
+ Arguments.of(String.valueOf(Integer.MAX_VALUE + 1)),
+ Arguments.of("anytext"),
+ Arguments.of(""),
+ Arguments.of("-278")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("notValidCases")
+ void testNotValid(String input) {
+ setInput(input);
+ pyramidPrinter.run();
+ printOut();
+ removeFromOutput("Please, input Pyramid height: ");
+ assertEquals("Only 1 non-negative integer is allowed as passed parameter", getOutputLines()[0]);
+ }
+
+ @Test
+ void testNull() {
+ setInput("0");
+ pyramidPrinter.run();
+ printOut();
+ removeFromOutput("Please, input Pyramid height: ");
+ assertEquals("", getOutputLines()[0]);
+ }
+
+}
diff --git a/src/test/java/homework_2/random_chars_table/RandomCharsTableTest.java b/src/test/java/homework_2/random_chars_table/RandomCharsTableTest.java
new file mode 100644
index 00000000..f7e4b2fb
--- /dev/null
+++ b/src/test/java/homework_2/random_chars_table/RandomCharsTableTest.java
@@ -0,0 +1,128 @@
+package homework_2.random_chars_table;
+
+import base.UnitBase;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class RandomCharsTableTest extends UnitBase {
+
+ RandomCharsTable rct = new RandomCharsTable();
+
+ // robots will replace human beings !
+ private void testInts(int length, int width) {
+ if (length <= 0 || width <= 0) {
+ throw new AssertionError();
+ }
+ }
+
+ private int testStr(String strategy) {
+ if (strategy.equalsIgnoreCase("odd")) {
+ return 1;
+ } else if (strategy.equalsIgnoreCase("even")) {
+ return 0;
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ // free for adding new valid values
+ private static Stream validCases() {
+ return Stream.of(
+ Arguments.of(5, 5, "odd"),
+ Arguments.of(7, 5, "even"),
+ Arguments.of(13, 15, "even"),
+ Arguments.of(2, 3, "odd"),
+ Arguments.of(1, 8, "odd"),
+ Arguments.of(1, 1, "even")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("validCases")
+ void testValid(int rows, int columns, String strategy) {
+ testInts(rows, columns);
+ int remainder = testStr(strategy);
+
+ setInput(
+ String.format(
+ "%d %d %s",
+ rows, columns, strategy
+ ));
+
+ rct.run();
+ printOut();
+ removeFromOutput("Input table length, table width, table strategy(even/odd): ");
+ for (int i = 0; i < rows; i++) {
+ String[] row = getOutputLines()[i].split(" ");
+ for (String el : row) {
+ assert el.charAt(0) == '|' || (el.charAt(0) >= 65 && el.charAt(0) <= 90);
+ }
+ }
+
+ // check result could be empty
+ try {
+ if (!getOutputLines()[rows].equals("")) {
+ String[] result = getOutputLines()[rows].split(" ");
+ for (int i = 3; i < result.length; i++) {
+ assert (result[i].charAt(0) % 2 == remainder);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return;
+ }
+ }
+
+ // free for adding new not valid cases in format Int, Int, String
+ private static Stream notValidCases() {
+ return Stream.of(
+ Arguments.of(-1, -2, "odd"),
+ Arguments.of(0, 0, "even"),
+ Arguments.of(1, 3, "omg"),
+ Arguments.of(1234, -2, "even")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("notValidCases")
+ void testNotValid(int rows, int columns, String strategy) {
+ setInput(
+ String.format(
+ "%d %d %s",
+ rows, columns, strategy
+ ));
+ rct.run();
+ printOut();
+ removeFromOutput("Input table length, table width, table strategy(even/odd): ");
+ assertEquals("Passed parameters should match the format [positive integer] [positive integer] [even|odd]",
+ getOutputLines()[0]);
+ }
+
+ @Test
+ void testFormatCount() {
+ setInput("1 2 even 12even");
+
+ rct.run();
+ printOut();
+ removeFromOutput("Input table length, table width, table strategy(even/odd): ");
+ assertEquals("Passed parameters should match the format [positive integer] [positive integer] [even|odd]",
+ getOutputLines()[0]);
+ }
+
+ @Test
+ void testEmpty() {
+ setInput("");
+
+ rct.run();
+ printOut();
+ removeFromOutput("Input table length, table width, table strategy(even/odd): ");
+ assertEquals("Passed parameters should match the format [positive integer] [positive integer] [even|odd]",
+ getOutputLines()[0]);
+ }
+
+}
diff --git a/src/test/java/homework_2/traffic_light/TrafficLightTest.java b/src/test/java/homework_2/traffic_light/TrafficLightTest.java
new file mode 100644
index 00000000..cbae6c2d
--- /dev/null
+++ b/src/test/java/homework_2/traffic_light/TrafficLightTest.java
@@ -0,0 +1,40 @@
+package homework_2.traffic_light;
+
+import base.UnitBase;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TrafficLightTest extends UnitBase {
+
+ TrafficLight trafficLight = new TrafficLight();
+
+ public static Stream testCases() {
+ return Stream.of(
+ Arguments.of("0", "[1;92mGREEN\u001B[0m"),
+ Arguments.of("101", "[1;91mRED\u001B[0m"),
+ Arguments.of("121", "[1;92mGREEN\u001B[0m"),
+ Arguments.of("155", "[1;93mYELLOW\u001B[0m"),
+ Arguments.of("86399", "[1;93mYELLOW\u001B[0m"),
+ Arguments.of("86400", "The day is over"),
+ Arguments.of("anytext", "Only 1 non-negative integer is allowed as passed parameter"),
+ Arguments.of("-1", "Only 1 non-negative integer is allowed as passed parameter"),
+ Arguments.of("", "Only 1 non-negative integer is allowed as passed parameter")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("testCases")
+ void testFactorial(String in, String expected) {
+ setInput(in);
+ trafficLight.run();
+ printOut();
+ removeFromOutput("Please, input seconds gone from the day start: ");
+ assertEquals(expected, getOutputLines()[0]);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/homework_4/custom_annotation/ResourcesPathTest.java b/src/test/java/homework_4/custom_annotation/ResourcesPathTest.java
new file mode 100644
index 00000000..72359d64
--- /dev/null
+++ b/src/test/java/homework_4/custom_annotation/ResourcesPathTest.java
@@ -0,0 +1,37 @@
+package homework_4.custom_annotation;
+
+import homework_4.custom_file_reader.CustomFileReader;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Constructor;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ResourcesPathTest {
+
+ @Test
+ void testAnnotation() {
+ Constructor[] constructors = CustomFileReader.class.getConstructors();
+ Constructor myConstructor = null;
+ for (Constructor constructor : constructors) {
+ if (constructor.isAnnotationPresent(ResourcesPath.class)) {
+ myConstructor = constructor;
+ break;
+ }
+ }
+
+ if (myConstructor != null) {
+ String constructorType = "Empty constructor";
+ if (myConstructor.getParameterCount() != 0) {
+ constructorType = "Constructor with parameters";
+ }
+ System.out.println(constructorType + " in " + CustomFileReader.class.getSimpleName() + " class " +
+ "has annotation @" + ResourcesPath.class.getSimpleName());
+ } else {
+ System.out.println("No annotated constructor found in " + CustomFileReader.class.getSimpleName());
+ throw new AssertionError();
+ }
+ assertTrue(myConstructor.isAnnotationPresent(ResourcesPath.class));
+ }
+
+}
diff --git a/src/test/java/homework_4/custom_file_reader/CustomFileReaderTest.java b/src/test/java/homework_4/custom_file_reader/CustomFileReaderTest.java
new file mode 100644
index 00000000..fdab46d9
--- /dev/null
+++ b/src/test/java/homework_4/custom_file_reader/CustomFileReaderTest.java
@@ -0,0 +1,113 @@
+package homework_4.custom_file_reader;
+
+import base.UnitBase;
+import homework_4.custom_file_reader.utils.DirectoryScan;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CustomFileReaderTest extends UnitBase {
+
+ CustomFileReader customFileReader = new CustomFileReader();
+ String correctPath = "./src/main/resources/custom_file_reader/";
+ String correctFileName = "file.txt";
+
+ @Test
+ void testValidRun1Method() {
+ customFileReader.run1();
+ String result = getOutput();
+ assertTrue(checkResult(result));
+ }
+
+ @Test
+ void testValidRun2Method() {
+ customFileReader.run2();
+ String result = getOutput();
+ assertTrue(checkResult(result));
+ }
+
+ @Test
+ void testValidRun3Method() {
+ customFileReader.run3();
+ String result = getOutput();
+ assertTrue(checkResult(result));
+ }
+
+ @Test
+ void testValidRun4Method() {
+ customFileReader.run4();
+ String result = getOutput();
+ assertTrue(checkResult(result));
+ }
+
+ @Test
+ void testNotExistingFileAsConstructorParameter() {
+ CustomFileReader cfr1 = new CustomFileReader(correctPath, "passwords.txt");
+ String expected = "File " + "passwords.txt" + " not found at: " + correctPath;
+ cfr1.run1();
+ String output = getOutput();
+ assertEquals(expected, output);
+ }
+
+ @Test
+ void testCorrectFileNameAndIncorrectFilePathAsConstuctorParameter() {
+ CustomFileReader cfr1 = new CustomFileReader("./src/", correctFileName);
+ String expected = "File " + correctFileName + " not found at: " + "./src/";
+ cfr1.run4();
+ String output = getOutput();
+ assertEquals(expected, output);
+ }
+
+ @Test
+ void testEmptyConstructor() {
+ new CustomFileReader();
+ String notExpected = "File " + correctFileName + " not found at: " + correctPath;
+ assertNotEquals(notExpected, getOutput());
+ }
+
+ @Test
+ void testShowDirMethod() {
+ CustomFileReader cfr1 = new CustomFileReader(correctPath, correctFileName);
+ cfr1.showDir();
+ String output = getOutput();
+
+ File directoryPath = new File(correctPath);
+ File[] filesList = directoryPath.listFiles();
+ StringBuilder tmp = new StringBuilder();
+ tmp.append(correctPath).append(" contains:").append("\n--- --- ---\n");
+ assertNotNull(filesList);
+ for (File file : filesList) {
+ tmp
+ .append(file.getName())
+ .append("\tat: ")
+ .append(file.getAbsolutePath())
+ .append("\n")
+ .append("---")
+ .append("\n");
+ }
+ tmp.append("--- --- ---");
+ String expected = String.valueOf(tmp);
+ assertEquals(expected, output);
+ }
+
+ @Test
+ void testEmptyDir() {
+ String hm3testDir = "./src/test/homework_3/";
+ String expected = hm3testDir + " is empty!";
+ DirectoryScan.scanDir(hm3testDir);
+ String output = getOutput();
+ assertEquals(expected, output);
+ }
+
+ @Test
+ void testIsInDir() {
+ assertTrue(DirectoryScan.isInDir(correctPath, correctFileName));
+ }
+
+ private boolean checkResult(String res) {
+ return !(res.contains(".") && res.contains(","));
+ }
+
+}
diff --git a/src/test/java/homework_4/singleton/SingletonMenuTest.java b/src/test/java/homework_4/singleton/SingletonMenuTest.java
new file mode 100644
index 00000000..b6160bfe
--- /dev/null
+++ b/src/test/java/homework_4/singleton/SingletonMenuTest.java
@@ -0,0 +1,16 @@
+package homework_4.singleton;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SingletonMenuTest {
+
+ @Test
+ void testCreatingInstancesOfSingletonClass() {
+ SingletonMenu expected = SingletonMenu.getInstance();
+ SingletonMenu current = SingletonMenu.getInstance();
+ assertSame(expected, current);
+ }
+
+}
diff --git a/src/test/java/homework_5/custom_regex_matcher/CustomRegexMatcherTest.java b/src/test/java/homework_5/custom_regex_matcher/CustomRegexMatcherTest.java
new file mode 100644
index 00000000..1b73436b
--- /dev/null
+++ b/src/test/java/homework_5/custom_regex_matcher/CustomRegexMatcherTest.java
@@ -0,0 +1,56 @@
+package homework_5.custom_regex_matcher;
+
+import base.UnitBase;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CustomRegexMatcherTest extends UnitBase {
+
+ @Test
+ void testValidPhoneNumber() {
+ String expected = "true";
+ setInput("+1 (267) 759-9000");
+ CustomRegexMatcher.run();
+ printOut();
+ assertEquals(expected, getOutput());
+ }
+
+ @Test
+ void testValidUnicodeInputNumber() {
+ String expected = "true";
+ setInput("\u002B\u0031 \u0028\u0032\u0036\u0037\u0029 \u0037\u0035\u0039\u002D\u0038\u0039\u0038\u0039");
+ CustomRegexMatcher.run();
+ printOut();
+ assertEquals(expected, getOutput());
+ }
+
+ public static Stream incorrectPhoneNumbersFormats() {
+ return Stream.of(
+ Arguments.of("+9 (2902) 123-4567"),
+ Arguments.of("not number at all, ho-ho"),
+ Arguments.of("-2 (123) 456-7890"),
+ Arguments.of("(+1) (123) 456-7890"),
+ Arguments.of("_12345678901"),
+ Arguments.of("+1 234 567-8909"),
+ Arguments.of("+1 (3.14) 742-7580"),
+ Arguments.of("\u1F606")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("incorrectPhoneNumbersFormats")
+ void testSetOfIncorrectPhoneNumbersFormats(String input) {
+ String expected = "false";
+ setInput(input);
+ CustomRegexMatcher.run();
+ printOut();
+ assertEquals(expected, getOutput());
+ }
+
+}
diff --git a/src/test/java/homework_5/power_of_number/PowerOfNumberTest.java b/src/test/java/homework_5/power_of_number/PowerOfNumberTest.java
new file mode 100644
index 00000000..47e7ac8f
--- /dev/null
+++ b/src/test/java/homework_5/power_of_number/PowerOfNumberTest.java
@@ -0,0 +1,63 @@
+package homework_5.power_of_number;
+
+import base.UnitBase;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class PowerOfNumberTest extends UnitBase {
+
+ PowerOfNumber powerOfNumber = new PowerOfNumber();
+
+ private static final String ERROR = "Only 2 non-negative integers are allowed";
+
+ @Test
+ void testPowerWithValidInput() {
+ String expected = convertDoubleToIntAndReturnAsString(Math.pow(3, 5));
+ setInput("3 5");
+ powerOfNumber.run();
+ printOut();
+ removeFromOutput("Please, input the values in format: number power\n");
+ assertEquals(expected, getOutputLines()[0]);
+ }
+
+ @Test
+ void testPowerWithValidInput_ZeroPowerOfZero() {
+ String expected = convertDoubleToIntAndReturnAsString(Math.pow(0, 0));
+ setInput("0 0");
+ powerOfNumber.run();
+ printOut();
+ removeFromOutput("Please, input the values in format: number power\n");
+ assertEquals(expected, getOutputLines()[0]);
+ }
+
+ public static Stream incorrectInputs() {
+ return Stream.of(
+ Arguments.of(""),
+ Arguments.of("1 2 3 4 str"),
+ Arguments.of("1 "),
+ Arguments.of("1.93 2.47"),
+ Arguments.of("999999999999999999 999999999999999999")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("incorrectInputs")
+ void testIncorrectInputs_ErrorExpected(String incorrectInput) {
+ setInput(incorrectInput);
+ powerOfNumber.run();
+ printOut();
+ removeFromOutput("Please, input the values in format: number power\n");
+ assertEquals(ERROR, getOutputLines()[0]);
+ }
+
+ private String convertDoubleToIntAndReturnAsString(double value) {
+ return String.valueOf((int) (value));
+ }
+
+}
diff --git a/src/test/java/homework_7/kitten_to_cat/KittenToCatTest.java b/src/test/java/homework_7/kitten_to_cat/KittenToCatTest.java
new file mode 100644
index 00000000..d9ac1eca
--- /dev/null
+++ b/src/test/java/homework_7/kitten_to_cat/KittenToCatTest.java
@@ -0,0 +1,61 @@
+package homework_7.kitten_to_cat;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class KittenToCatTest {
+
+ private static final String expected = Cat.class.getSimpleName();
+
+ @Test
+ void testKittenToCat() {
+ Kitten kitten = new Kitten("Joe Bezos", 3, "Amazonerre");
+ KittenToCatFunction kittyFoo = kitten1 -> new Cat("Joseph Bezos", kitten.getAge() + 2);
+ Cat cat = kittyFoo.grow(kitten);
+ assertEquals(expected, cat.getClass().getSimpleName());
+ }
+
+ @Test
+ void testKittenInheritedToCatCast() {
+
+ class JustBornKitten extends Kitten {
+
+ private final LocalDate bornDate;
+
+ public JustBornKitten(String name, int age, String breed) {
+ super(name, age, breed);
+ bornDate = LocalDate.now();
+ }
+
+ }
+
+ JustBornKitten justBornKitten = new JustBornKitten("Billy", 0, "Microkitty");
+ KittenToCatFunction kittyFoo = kitten -> new Cat("Bill Gates", justBornKitten.getAge() + 3);
+ Cat cat = kittyFoo.grow(justBornKitten);
+ assertEquals(expected, cat.getClass().getSimpleName());
+ }
+
+ @Test
+ void testKittenToInheritedCatCast() {
+
+ class WildCat extends Cat {
+
+ private final String location;
+
+ public WildCat(String name, int age) {
+ super(name, age);
+ this.location = "Africa";
+ }
+
+ }
+
+ Kitten kitty = new Kitten("Elon", 1, "Nikoleslie");
+ KittenToCatFunction kittyFoo = kit -> new WildCat("Elon Mask", kitty.getAge() + 3);
+ WildCat wildCat = (WildCat) kittyFoo.grow(kitty);
+ assertEquals(wildCat.getClass().getSimpleName(), wildCat.getClass().getSimpleName());
+ }
+
+}