diff --git a/pom.xml b/pom.xml
index b3c4015..23d4bb9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,7 @@
vol6
vol7
vol8
+ vol_
diff --git a/vol8/src/test/java/ru/mifi/practice/vol8/regexp/machine/HippopotamusTest.java b/vol8/src/test/java/ru/mifi/practice/vol8/regexp/machine/HippopotamusTest.java
index d26ef05..b4a35ae 100644
--- a/vol8/src/test/java/ru/mifi/practice/vol8/regexp/machine/HippopotamusTest.java
+++ b/vol8/src/test/java/ru/mifi/practice/vol8/regexp/machine/HippopotamusTest.java
@@ -74,7 +74,7 @@ public int hashCode() {
@Override
public String toString() {
- return String.valueOf(index);
+ return "Hippo{" + index + "}";
}
}
diff --git a/vol_/pom.xml b/vol_/pom.xml
new file mode 100644
index 0000000..e874671
--- /dev/null
+++ b/vol_/pom.xml
@@ -0,0 +1,21 @@
+
+
+ 4.0.0
+
+ ru.mifi.practice
+ AaDS
+ 2024.2
+ ../pom.xml
+
+ vol_
+ vol_
+ Модуль №_.
+
+
+
+ com.google.guava
+ guava
+
+
+
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/AdventureGame.java b/vol_/src/main/java/ru/mifi/practice/voln/AdventureGame.java
new file mode 100644
index 0000000..3b942da
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/AdventureGame.java
@@ -0,0 +1,169 @@
+package ru.mifi.practice.voln;
+
+import lombok.Getter;
+import ru.mifi.practice.voln.logic.Item;
+import ru.mifi.practice.voln.logic.Person;
+import ru.mifi.practice.voln.logic.Updatable;
+import ru.mifi.practice.voln.transmit.Output;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+
+public final class AdventureGame implements Updatable, Updatable.Context {
+ private static final int GAME_LINE_LENGTH = 20;
+ private final Random random = new Random(new Date().getTime());
+ private final Object[] gameLine = new Object[GAME_LINE_LENGTH + 1];
+ private final Output output;
+ private final Person.Player player;
+ private int index;
+ @Getter
+ private boolean running;
+
+ public AdventureGame(Output output, Person.Player player) {
+ this.output = output;
+ this.player = player;
+ running = true;
+ nextGameLine();
+ }
+
+ private void nextGameLine() {
+ Arrays.fill(gameLine, null);
+ index = 0;
+ gameLine[0] = player;
+ int items = random.nextInt(3) + 5;
+ int enemies = random.nextInt(7) + 2;
+ for (int i = 0; i < items; i++) {
+ generateElement(Type.ITEM);
+ }
+ for (int i = 0; i < enemies; i++) {
+ generateElement(Type.ENEMY);
+ }
+ }
+
+ private void generateElement(Type type) {
+ int index = 2;
+ while (gameLine[index] != null) {
+ index = random.nextInt(gameLine.length - 3) + 2;
+ }
+ switch (type) {
+ case ENEMY -> {
+ gameLine[index] = new Person.Mob("Paul", 80, index,
+ new Item.DamageItem(random.nextInt(10) + 5), false);
+ }
+ case EMPTY -> {
+ }
+ case ITEM -> {
+ if (random.nextBoolean()) {
+ gameLine[index] = new Item.Health(random.nextInt(30) + 15);
+ } else {
+ gameLine[index] = new Item.Hummer();
+ }
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + type);
+ }
+ }
+
+ void update() {
+ update(this);
+ }
+
+ @Override
+ public void update(Context context) {
+ for (Object o : gameLine) {
+ if (o instanceof Updatable update) {
+ update.update(context);
+ }
+ }
+ }
+
+ @Override
+ public void died(Person person) {
+ if (person instanceof Person.Player) {
+ output.println("Game over");
+ running = false;
+ } else if (person instanceof Person.Mob mob) {
+ gameLine[index] = null;
+ index = mob.getIndex();
+ gameLine[index] = player;
+ player.addInventory(new Item.Health(100 - player.health()));
+ }
+ }
+
+ private View viewIndex(int index) {
+ Objects.checkIndex(index, gameLine.length);
+ Object object = gameLine[index];
+ if (object instanceof Item item) {
+ return new View(Type.ITEM, item);
+ } else if (object instanceof Person.Mob mob) {
+ return new View(Type.ENEMY, mob);
+ } else if (object instanceof Person.Player player) {
+ return new View(Type.PLAYER, player);
+ }
+ return new View(Type.EMPTY, null);
+ }
+
+ @Override
+ public View view(Person.Player player) {
+ return viewIndex(index + 1);
+ }
+
+ @Override
+ public View view(Person.Mob mob) {
+ int i = mob.getIndex();
+ Objects.checkIndex(i - 1, gameLine.length);
+ return viewIndex(i - 1);
+ }
+
+ @Override
+ public void hit(Person person, Item item) {
+ output.println("Hit " + person + " on " + item);
+ }
+
+ public View playerView() {
+ return view(player);
+ }
+
+ public void forward() {
+ if (index + 1 < gameLine.length && gameLine[index + 1] instanceof Person.Mob) {
+ output.println("We cant move forward");
+ return;
+ }
+ index++;
+ if (index >= gameLine.length) {
+ nextGameLine();
+ } else {
+ gameLine[index - 1] = null;
+ gameLine[index] = player;
+ }
+ }
+
+ public void attack() {
+ View view = playerView();
+ if (view.type() == Type.ENEMY) {
+ Person.Mob mob = (Person.Mob) view.element();
+ mob.hit(player.getSelectedItem(), this);
+ } else {
+ output.println("We cant kick empty space");
+ }
+ }
+
+ public List- listItems() {
+ return player.items();
+ }
+
+ public void selectItem(int itemIndex) {
+ player.selectItem(itemIndex, this);
+ }
+
+ public void catchItem() {
+ Updatable.View view = playerView();
+ if (view.type() == Updatable.Type.ITEM) {
+ player.addInventory((Item)view.element());
+ gameLine[index + 1] = null;
+ forward();
+ }
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/Main.java b/vol_/src/main/java/ru/mifi/practice/voln/Main.java
new file mode 100644
index 0000000..38ee121
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/Main.java
@@ -0,0 +1,80 @@
+package ru.mifi.practice.voln;
+
+import ru.mifi.practice.voln.logic.Item;
+import ru.mifi.practice.voln.logic.Person;
+import ru.mifi.practice.voln.logic.Updatable;
+import ru.mifi.practice.voln.transmit.Transmit;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@SuppressWarnings("PMD.LooseCoupling")
+public abstract class Main {
+ public static void main(String[] args) {
+ Transmit transmit = new Transmit.Standard();
+ transmit.println("Input f - move front");
+ transmit.println("Input v - view front");
+ transmit.println("Input s - select item");
+ transmit.println("Input c - catch item");
+ transmit.println("Input a - attack");
+ transmit.println("Input w - waiting");
+ transmit.println("Input q - quit");
+ Person.Player player = new Person.Player("Unknown");
+ AdventureGame game = new AdventureGame(transmit, player);
+ LinkedList commands = new LinkedList<>();
+ boolean isRunning = true;
+ while (isRunning && game.isRunning()) {
+ if (commands.isEmpty()) {
+ transmit.print("Input player command: ");
+ String input = transmit.readText();
+ input.chars().forEach(c -> commands.add((char) c));
+ }
+ Character c = commands.removeFirst();
+ switch (c) {
+ case 'q': {
+ isRunning = false;
+ break;
+ }
+ case 'w': {
+ transmit.println("waiting...");
+ break;
+ }
+ case 'a': {
+ game.attack();
+ break;
+ }
+ case 'v': {
+ Updatable.View view = game.playerView();
+ transmit.println(view.toString());
+ break;
+ }
+ case 'c': {
+ game.catchItem();
+ break;
+ }
+ case 's': {
+ List
- items = game.listItems();
+ if (!items.isEmpty()) {
+ for (int i = 0; i < items.size(); i++) {
+ Item item = items.get(i);
+ transmit.println("[" + i + "] " + item.toString());
+ }
+ transmit.print("Select item or -1 skip: ");
+ var input = transmit.readInt().orElse(-1);
+ game.selectItem(input);
+ }
+ break;
+ }
+ case 'f': {
+ game.forward();
+ break;
+ }
+ default: {
+ transmit.println("Invalid command");
+ }
+ }
+ game.update();
+ }
+ transmit.println("game completed!");
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/logic/Item.java b/vol_/src/main/java/ru/mifi/practice/voln/logic/Item.java
new file mode 100644
index 0000000..ce26ac2
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/logic/Item.java
@@ -0,0 +1,60 @@
+package ru.mifi.practice.voln.logic;
+
+import ru.mifi.practice.voln.logic.Updatable.Context;
+
+public interface Item {
+
+ int damage();
+
+ interface Once extends Item {
+ void apply(Person person, Context context);
+ }
+
+ final class Health implements Once {
+ private final int health;
+
+ public Health(int health) {
+ this.health = health;
+ }
+
+ @Override
+ public int damage() {
+ return 1;
+ }
+
+ @Override
+ public void apply(Person person, Context context) {
+ person.healthUp(health);
+ }
+
+ @Override
+ public String toString() {
+ return "H" + health;
+ }
+ }
+
+ class DamageItem implements Item {
+ private final int damage;
+
+ public DamageItem(int damage) {
+ this.damage = damage;
+ }
+
+ @Override
+ public int damage() {
+ return damage;
+ }
+
+ @Override
+ public String toString() {
+ return "D" + damage;
+ }
+ }
+
+ final class Hummer extends DamageItem {
+
+ public Hummer() {
+ super(10);
+ }
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/logic/Person.java b/vol_/src/main/java/ru/mifi/practice/voln/logic/Person.java
new file mode 100644
index 0000000..817370f
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/logic/Person.java
@@ -0,0 +1,130 @@
+package ru.mifi.practice.voln.logic;
+
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public interface Person extends Updatable {
+ void hit(Item item, Context context);
+
+ void healthUp(int health);
+
+ int health();
+
+ abstract class AbstractPerson implements Person {
+ private final String name;
+ private int hitPoints;
+ private int updateCount;
+
+ protected AbstractPerson(String name, int hitPoints) {
+ this.name = name;
+ this.hitPoints = hitPoints;
+ }
+
+ @Override
+ public int health() {
+ return hitPoints;
+ }
+
+ @Override
+ public void update(Context context) {
+ ++updateCount;
+ if (updateCount % 1000 == 0 && hitPoints < 50) {
+ ++hitPoints;
+ }
+ }
+
+ @Override
+ public void hit(Item item, Context context) {
+ context.hit(this, item);
+ hitPoints -= item.damage();
+ if (hitPoints <= 0) {
+ context.died(this);
+ }
+ }
+
+ @Override
+ public void healthUp(int health) {
+ hitPoints += health;
+ }
+
+ @Override
+ public String toString() {
+ return name + ": " + hitPoints;
+ }
+ }
+
+ final class Mob extends AbstractPerson {
+ @Getter
+ private final int index;
+ private final Item.DamageItem damage;
+ //NOTICE: Если агрессивный, то бьет первым
+ private boolean aggressive;
+ private boolean toArge;
+
+ public Mob(String name, int hitPoints, int index, Item.DamageItem damage, boolean aggressive) {
+ super(name, hitPoints);
+ this.index = index;
+ this.damage = damage;
+ this.aggressive = aggressive;
+ this.toArge = false;
+ }
+
+ @Override
+ public void update(Context context) {
+ super.update(context);
+ View view = context.view(this);
+ if (toArge) {
+ if (view != null && view.type() == Type.PLAYER) {
+ Player player = (Player) view.element();
+ player.hit(damage, context);
+ }
+ } else if (aggressive) {
+ if (view != null && view.type() == Type.PLAYER) {
+ toArge = true;
+ }
+ }
+ }
+
+ @Override
+ public void hit(Item item, Context context) {
+ super.hit(item, context);
+ toArge = true;
+ }
+ }
+
+ final class Player extends AbstractPerson {
+ private static final Item FIST = new Item.DamageItem(5);
+ private final List
- inventory = new ArrayList<>();
+ @Getter
+ private Item selectedItem;
+
+ public Player(String name) {
+ super(name, 100);
+ selectedItem = FIST;
+ }
+
+ public void selectItem(int item, Context context) {
+ if (item < 0 || item >= inventory.size()) {
+ selectedItem = FIST;
+ return;
+ }
+ Item element = inventory.get(item);
+ if (element instanceof Item.Once once) {
+ once.apply(this, context);
+ inventory.remove(item);
+ } else {
+ selectedItem = element;
+ }
+ }
+
+ public List
- items() {
+ return inventory;
+ }
+
+ public void addInventory(Item item) {
+ inventory.add(item);
+ }
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/logic/Updatable.java b/vol_/src/main/java/ru/mifi/practice/voln/logic/Updatable.java
new file mode 100644
index 0000000..7714d9d
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/logic/Updatable.java
@@ -0,0 +1,27 @@
+package ru.mifi.practice.voln.logic;
+
+public interface Updatable {
+ void update(Context context);
+
+ interface Context {
+
+ void died(Person person);
+
+ View view(Person.Player player);
+
+ View view(Person.Mob mob);
+
+ void hit(Person person, Item item);
+ }
+
+ record View(Type type, Object element) {
+ @Override
+ public String toString() {
+ return "[" + type.toString() + "]" + (element == null ? "" : element.toString());
+ }
+ }
+
+ enum Type {
+ ENEMY, EMPTY, ITEM, PLAYER
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/transmit/Input.java b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Input.java
new file mode 100644
index 0000000..3ac6ad8
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Input.java
@@ -0,0 +1,16 @@
+package ru.mifi.practice.voln.transmit;
+
+import java.util.Optional;
+
+public interface Input {
+
+ String readText();
+
+ default Optional readInt() {
+ try {
+ return Optional.of(Integer.parseInt(readText()));
+ } catch (Exception e) {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/transmit/Output.java b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Output.java
new file mode 100644
index 0000000..945d368
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Output.java
@@ -0,0 +1,9 @@
+package ru.mifi.practice.voln.transmit;
+
+public interface Output {
+ void print(String format, Object... args);
+
+ default void println(String format, Object... args) {
+ print(format + "%n", args);
+ }
+}
diff --git a/vol_/src/main/java/ru/mifi/practice/voln/transmit/Transmit.java b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Transmit.java
new file mode 100644
index 0000000..f16f331
--- /dev/null
+++ b/vol_/src/main/java/ru/mifi/practice/voln/transmit/Transmit.java
@@ -0,0 +1,33 @@
+package ru.mifi.practice.voln.transmit;
+
+import java.io.PrintStream;
+import java.util.Scanner;
+
+public interface Transmit extends Input, Output {
+
+ final class Standard implements Transmit {
+ private final PrintStream output;
+ private final Scanner input;
+
+ Standard(PrintStream output, Scanner input) {
+ this.output = output;
+ this.input = input;
+ }
+
+ public Standard() {
+ this(new PrintStream(System.out), new Scanner(System.in));
+ }
+
+ @Override
+ public String readText() {
+ return input.nextLine();
+ }
+
+ @Override
+ public void print(String format, Object... args) {
+ output.printf(format, args);
+ //NOTICE: Не очень хорошо
+ output.flush();
+ }
+ }
+}
diff --git a/vol_/src/test/java/ru/mifi/practice/voln/transmit/TransmitTest.java b/vol_/src/test/java/ru/mifi/practice/voln/transmit/TransmitTest.java
new file mode 100644
index 0000000..984138e
--- /dev/null
+++ b/vol_/src/test/java/ru/mifi/practice/voln/transmit/TransmitTest.java
@@ -0,0 +1,66 @@
+package ru.mifi.practice.voln.transmit;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.NoSuchElementException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@DisplayName("Transmit")
+class TransmitTest {
+ @DisplayName("Standard.readText")
+ @Test
+ void standardReadText() {
+ InputStream in = System.in;
+ try {
+ System.setIn(new ByteArrayInputStream(("TEST\n").getBytes()));
+ Transmit.Standard standard = new Transmit.Standard();
+ assertEquals("TEST", standard.readText());
+ assertThrows(NoSuchElementException.class, standard::readText);
+ } finally {
+ System.setIn(in);
+ }
+ }
+
+ @DisplayName("Standard.readInt")
+ @Test
+ void standardReadInt() {
+ InputStream in = System.in;
+ try {
+ System.setIn(new ByteArrayInputStream(("10\n").getBytes()));
+ Transmit.Standard standard = new Transmit.Standard();
+ assertEquals(10, standard.readInt().orElseThrow());
+ System.setIn(new ByteArrayInputStream(("100\n101\n102\r\n\r\n\r\n").getBytes()));
+ standard = new Transmit.Standard();
+ assertEquals(100, standard.readInt().orElseThrow());
+ assertEquals(101, standard.readInt().orElseThrow());
+ assertEquals(102, standard.readInt().orElseThrow());
+ assertFalse(standard.readInt().isPresent());
+ } finally {
+ System.setIn(in);
+ }
+ }
+
+ @DisplayName("Standard.print")
+ @Test
+ void standardPrint() {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ PrintStream output = new PrintStream(buffer);
+ Transmit.Standard standard = new Transmit.Standard(output, null);
+ standard.print("TEST");
+ assertEquals("TEST", buffer.toString());
+ buffer.reset();
+ standard.print("Hello %s", "World");
+ assertEquals("Hello World", buffer.toString());
+ buffer.reset();
+ standard.println("Hello %s", "World");
+ assertEquals(String.format("Hello World%n"), buffer.toString());
+ }
+}