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()); + } +}