queue = new LinkedBlockingQueue<>();
+ Random rng = new Random();
+ for (int i = 0; i < 10; i++) {
+ // Gives a random speed to each character to generate different waiting times
+ var weapon = new Weapon("", 0, rng.nextInt(50), WeaponType.KNIFE);
+ var character = new PlayerCharacter(Integer.toString(i), queue,
+ CharacterClass.THIEF);
+ character.equip(weapon);
+ character.waitTurn();
+ }
+ // Waits for 6 seconds to ensure that all characters have finished waiting
+ Thread.sleep(6000);
+ while (!queue.isEmpty()) {
+ // Pops and prints the names of the characters of the queue to illustrate the turns
+ // order
+ System.out.println(queue.poll().getName());
+ }
+ }
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/gui/FinalReality.java b/src/main/java/com/github/cc3002/finalreality/gui/FinalReality.java
new file mode 100644
index 0000000..91de482
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/gui/FinalReality.java
@@ -0,0 +1,36 @@
+package com.github.cc3002.finalreality.gui;
+
+import javafx.application.Application;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+
+/**
+ * Main entry point for the application.
+ *
+ *
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public class FinalReality extends Application {
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ @Override
+ public void start(Stage primaryStage) {
+ primaryStage.setTitle("Final reality");
+
+ Label label = new Label("This will be a game sometime");
+ label.setAlignment(Pos.CENTER);
+
+ // This sets the size of the Scene to be 400px wide, 200px high
+ Scene scene = new Scene(label, 400, 200);
+ primaryStage.setScene(scene);
+
+ primaryStage.show();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/cc3002/finalreality/model/character/AbstractCharacter.java b/src/main/java/com/github/cc3002/finalreality/model/character/AbstractCharacter.java
new file mode 100644
index 0000000..671da1e
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/character/AbstractCharacter.java
@@ -0,0 +1,75 @@
+package com.github.cc3002.finalreality.model.character;
+
+import com.github.cc3002.finalreality.model.character.player.CharacterClass;
+import com.github.cc3002.finalreality.model.character.player.PlayerCharacter;
+import com.github.cc3002.finalreality.model.weapon.Weapon;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An abstract class that holds the common behaviour of all the characters in the game.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public abstract class AbstractCharacter implements ICharacter {
+
+ protected final BlockingQueue turnsQueue;
+ protected final String name;
+ private final CharacterClass characterClass;
+ private Weapon equippedWeapon = null;
+ private ScheduledExecutorService scheduledExecutor;
+
+ protected AbstractCharacter(@NotNull BlockingQueue turnsQueue,
+ @NotNull String name, CharacterClass characterClass) {
+ this.turnsQueue = turnsQueue;
+ this.name = name;
+ this.characterClass = characterClass;
+ }
+
+ @Override
+ public void waitTurn() {
+ scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
+ if (this instanceof PlayerCharacter) {
+ scheduledExecutor
+ .schedule(this::addToQueue, equippedWeapon.getWeight() / 10, TimeUnit.SECONDS);
+ } else {
+ var enemy = (Enemy) this;
+ scheduledExecutor
+ .schedule(this::addToQueue, enemy.getWeight() / 10, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Adds this character to the turns queue.
+ */
+ private void addToQueue() {
+ turnsQueue.add(this);
+ scheduledExecutor.shutdown();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void equip(Weapon weapon) {
+ if (this instanceof PlayerCharacter) {
+ this.equippedWeapon = weapon;
+ }
+ }
+
+ @Override
+ public Weapon getEquippedWeapon() {
+ return equippedWeapon;
+ }
+
+ @Override
+ public CharacterClass getCharacterClass() {
+ return characterClass;
+ }
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/character/Enemy.java b/src/main/java/com/github/cc3002/finalreality/model/character/Enemy.java
new file mode 100644
index 0000000..e32598c
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/character/Enemy.java
@@ -0,0 +1,51 @@
+package com.github.cc3002.finalreality.model.character;
+
+import com.github.cc3002.finalreality.model.character.player.CharacterClass;
+import java.util.Objects;
+import java.util.concurrent.BlockingQueue;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A class that holds all the information of a single enemy of the game.
+ *
+ * @author Ignacio Slater Muñoz
+ * @author
+ */
+public class Enemy extends AbstractCharacter {
+
+ private final int weight;
+
+ /**
+ * Creates a new enemy with a name, a weight and the queue with the characters ready to
+ * play.
+ */
+ public Enemy(@NotNull final String name, final int weight,
+ @NotNull final BlockingQueue turnsQueue) {
+ super(turnsQueue, name, CharacterClass.ENEMY);
+ this.weight = weight;
+ }
+
+ /**
+ * Returns the weight of this enemy.
+ */
+ public int getWeight() {
+ return weight;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Enemy)) {
+ return false;
+ }
+ final Enemy enemy = (Enemy) o;
+ return getWeight() == enemy.getWeight();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getWeight());
+ }
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/character/ICharacter.java b/src/main/java/com/github/cc3002/finalreality/model/character/ICharacter.java
new file mode 100644
index 0000000..f76b08f
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/character/ICharacter.java
@@ -0,0 +1,40 @@
+package com.github.cc3002.finalreality.model.character;
+
+import com.github.cc3002.finalreality.model.character.player.CharacterClass;
+import com.github.cc3002.finalreality.model.weapon.Weapon;
+
+/**
+ * This represents a character from the game.
+ * A character can be controlled by the player or by the CPU (an enemy).
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public interface ICharacter {
+
+ /**
+ * Sets a scheduled executor to make this character (thread) wait for {@code speed / 10}
+ * seconds before adding the character to the queue.
+ */
+ void waitTurn();
+
+ /**
+ * Returns this character's name.
+ */
+ String getName();
+
+ /**
+ * Equips a weapon to the character.
+ */
+ void equip(Weapon weapon);
+
+ /**
+ * Return this character's equipped weapon.
+ */
+ Weapon getEquippedWeapon();
+
+ /**
+ * Returns this character's class.
+ */
+ CharacterClass getCharacterClass();
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/character/player/CharacterClass.java b/src/main/java/com/github/cc3002/finalreality/model/character/player/CharacterClass.java
new file mode 100644
index 0000000..8f28f26
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/character/player/CharacterClass.java
@@ -0,0 +1,11 @@
+package com.github.cc3002.finalreality.model.character.player;
+
+/**
+ * Enumeration of the classes a player character may have.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public enum CharacterClass {
+ KNIGHT, ENGINEER, THIEF, BLACK_MAGE, WHITE_MAGE, ENEMY
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/character/player/PlayerCharacter.java b/src/main/java/com/github/cc3002/finalreality/model/character/player/PlayerCharacter.java
new file mode 100644
index 0000000..3985abe
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/character/player/PlayerCharacter.java
@@ -0,0 +1,50 @@
+package com.github.cc3002.finalreality.model.character.player;
+
+import com.github.cc3002.finalreality.model.character.AbstractCharacter;
+import com.github.cc3002.finalreality.model.character.ICharacter;
+import java.util.Objects;
+import java.util.concurrent.BlockingQueue;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A class that holds all the information of a single character of the game.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public class PlayerCharacter extends AbstractCharacter {
+
+ /**
+ * Creates a new character.
+ *
+ * @param name
+ * the character's name
+ * @param turnsQueue
+ * the queue with the characters waiting for their turn
+ * @param characterClass
+ * the class of this character
+ */
+ public PlayerCharacter(@NotNull String name,
+ @NotNull BlockingQueue turnsQueue,
+ final CharacterClass characterClass) {
+ super(turnsQueue, name, characterClass);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getCharacterClass());
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PlayerCharacter)) {
+ return false;
+ }
+ final PlayerCharacter that = (PlayerCharacter) o;
+ return getCharacterClass() == that.getCharacterClass()
+ && getName().equals(that.getName());
+ }
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/weapon/Weapon.java b/src/main/java/com/github/cc3002/finalreality/model/weapon/Weapon.java
new file mode 100644
index 0000000..dd2c93f
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/weapon/Weapon.java
@@ -0,0 +1,66 @@
+package com.github.cc3002.finalreality.model.weapon;
+
+import java.util.Objects;
+
+/**
+ * A class that holds all the information of a weapon.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public class Weapon {
+
+ private final String name;
+ private final int damage;
+ private final int weight;
+ private final WeaponType type;
+
+ /**
+ * Creates a weapon with a name, a base damage, speed and it's type.
+ *
+ * @see WeaponType
+ */
+ public Weapon(final String name, final int damage, final int weight,
+ final WeaponType type) {
+ this.name = name;
+ this.damage = damage;
+ this.weight = weight;
+ this.type = type;
+ }
+
+ private String getName() {
+ return name;
+ }
+
+ private int getDamage() {
+ return damage;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ private WeaponType getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Weapon)) {
+ return false;
+ }
+ final Weapon weapon = (Weapon) o;
+ return getDamage() == weapon.getDamage() &&
+ getWeight() == weapon.getWeight() &&
+ getName().equals(weapon.getName()) &&
+ getType() == weapon.getType();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getName(), getDamage(), getWeight(), getType());
+ }
+}
diff --git a/src/main/java/com/github/cc3002/finalreality/model/weapon/WeaponType.java b/src/main/java/com/github/cc3002/finalreality/model/weapon/WeaponType.java
new file mode 100644
index 0000000..1785d95
--- /dev/null
+++ b/src/main/java/com/github/cc3002/finalreality/model/weapon/WeaponType.java
@@ -0,0 +1,11 @@
+package com.github.cc3002.finalreality.model.weapon;
+
+/**
+ * Enumeration of all the weapon types.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ */
+public enum WeaponType {
+ SWORD, AXE, KNIFE, STAFF, BOW
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..edd4fbb
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,5 @@
+module finalreality.main {
+ exports com.github.cc3002.finalreality.gui;
+ requires javafx.controls;
+ requires org.jetbrains.annotations;
+}
\ No newline at end of file
diff --git a/src/test/java/com/github/cc3002/finalreality/model/character/AbstractCharacterTest.java b/src/test/java/com/github/cc3002/finalreality/model/character/AbstractCharacterTest.java
new file mode 100644
index 0000000..5ecd285
--- /dev/null
+++ b/src/test/java/com/github/cc3002/finalreality/model/character/AbstractCharacterTest.java
@@ -0,0 +1,69 @@
+package com.github.cc3002.finalreality.model.character;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import com.github.cc3002.finalreality.model.weapon.Weapon;
+import com.github.cc3002.finalreality.model.weapon.WeaponType;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Abstract class containing the common tests for all the types of characters.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ * @see ICharacter
+ */
+public abstract class AbstractCharacterTest {
+
+ protected BlockingQueue turns;
+ protected List testCharacters;
+ protected Weapon testWeapon;
+
+ /**
+ * Checks that the character waits the appropriate amount of time for it's turn.
+ */
+ @Test
+ void waitTurnTest() {
+ Assertions.assertTrue(turns.isEmpty());
+ tryToEquip(testCharacters.get(0));
+ testCharacters.get(0).waitTurn();
+ try {
+ // Thread.sleep is not accurate so this values may be changed to adjust the
+ // acceptable error margin.
+ // We're testing that the character waits approximately 1 second.
+ Thread.sleep(900);
+ Assertions.assertEquals(0, turns.size());
+ Thread.sleep(200);
+ Assertions.assertEquals(1, turns.size());
+ Assertions.assertEquals(testCharacters.get(0), turns.peek());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void tryToEquip(ICharacter character) {
+ character.equip(testWeapon);
+ }
+
+ protected void checkConstruction(final ICharacter expectedCharacter,
+ final ICharacter testEqualCharacter,
+ final ICharacter sameClassDifferentCharacter,
+ final ICharacter differentClassCharacter) {
+ assertEquals(expectedCharacter, testEqualCharacter);
+ assertNotEquals(sameClassDifferentCharacter, testEqualCharacter);
+ assertNotEquals(testEqualCharacter, differentClassCharacter);
+ assertEquals(expectedCharacter.hashCode(), testEqualCharacter.hashCode());
+ }
+
+ protected void basicSetUp() {
+ turns = new LinkedBlockingQueue<>();
+ testWeapon = new Weapon("Test", 15, 10, WeaponType.AXE);
+ testCharacters = new ArrayList<>();
+ }
+}
diff --git a/src/test/java/com/github/cc3002/finalreality/model/character/EnemyTest.java b/src/test/java/com/github/cc3002/finalreality/model/character/EnemyTest.java
new file mode 100644
index 0000000..f39345f
--- /dev/null
+++ b/src/test/java/com/github/cc3002/finalreality/model/character/EnemyTest.java
@@ -0,0 +1,25 @@
+package com.github.cc3002.finalreality.model.character;
+
+import com.github.cc3002.finalreality.model.character.player.CharacterClass;
+import com.github.cc3002.finalreality.model.character.player.PlayerCharacter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class EnemyTest extends AbstractCharacterTest {
+
+ private static final String ENEMY_NAME = "Goblin";
+
+ @BeforeEach
+ void setUp() {
+ basicSetUp();
+ testCharacters.add(new Enemy(ENEMY_NAME, 10, turns));
+ }
+
+ @Test
+ void constructorTest() {
+ checkConstruction(new Enemy(ENEMY_NAME, 10, turns),
+ testCharacters.get(0),
+ new Enemy(ENEMY_NAME, 11, turns),
+ new PlayerCharacter(ENEMY_NAME, turns, CharacterClass.THIEF));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/github/cc3002/finalreality/model/character/PlayerCharacterTest.java b/src/test/java/com/github/cc3002/finalreality/model/character/PlayerCharacterTest.java
new file mode 100644
index 0000000..31f7e2b
--- /dev/null
+++ b/src/test/java/com/github/cc3002/finalreality/model/character/PlayerCharacterTest.java
@@ -0,0 +1,82 @@
+package com.github.cc3002.finalreality.model.character;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import com.github.cc3002.finalreality.model.character.player.CharacterClass;
+import com.github.cc3002.finalreality.model.character.player.PlayerCharacter;
+import java.util.EnumMap;
+import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Set of tests for the {@code GameCharacter} class.
+ *
+ * @author Ignacio Slater Muñoz.
+ * @author
+ * @see PlayerCharacter
+ */
+class PlayerCharacterTest extends AbstractCharacterTest {
+
+ private static final String BLACK_MAGE_NAME = "Vivi";
+ private static final String KNIGHT_NAME = "Adelbert";
+ private static final String WHITE_MAGE_NAME = "Eiko";
+ private static final String ENGINEER_NAME = "Cid";
+ private static final String THIEF_NAME = "Zidane";
+ private Map characterNames;
+
+ /**
+ * Setup method.
+ * Creates a new character named Vivi with 10 speed and links it to a turn queue.
+ */
+ @BeforeEach
+ void setUp() {
+ super.basicSetUp();
+
+ characterNames = new EnumMap<>(CharacterClass.class);
+ characterNames.put(CharacterClass.BLACK_MAGE, BLACK_MAGE_NAME);
+ characterNames.put(CharacterClass.KNIGHT, KNIGHT_NAME);
+ characterNames.put(CharacterClass.WHITE_MAGE, WHITE_MAGE_NAME);
+ characterNames.put(CharacterClass.ENGINEER, ENGINEER_NAME);
+ characterNames.put(CharacterClass.THIEF, THIEF_NAME);
+
+ for (var characterClass :
+ characterNames.keySet()) {
+ testCharacters.add(
+ new PlayerCharacter(characterNames.get(characterClass), turns, characterClass));
+ }
+ }
+
+ /**
+ * Checks that the class' constructor and equals method works properly.
+ */
+ @Test
+ void constructorTest() {
+ var enemy = new Enemy("Enemy", 10, turns);
+ for (var character :
+ testCharacters) {
+ var characterClass = character.getCharacterClass();
+ var characterName = characterNames.get(characterClass);
+ checkConstruction(new PlayerCharacter(characterName, turns, characterClass),
+ character,
+ new PlayerCharacter("Test", turns, characterClass),
+ new PlayerCharacter(characterName, turns,
+ characterClass == CharacterClass.THIEF ? CharacterClass.BLACK_MAGE
+ : CharacterClass.THIEF));
+ assertNotEquals(character, enemy);
+ }
+
+ }
+
+ @Test
+ void equipWeaponTest() {
+ for (var character :
+ testCharacters) {
+ assertNull(character.getEquippedWeapon());
+ character.equip(testWeapon);
+ assertEquals(testWeapon, character.getEquippedWeapon());
+ }
+ }
+}
diff --git a/src/test/java/com/github/cc3002/finalreality/model/weapon/WeaponTest.java b/src/test/java/com/github/cc3002/finalreality/model/weapon/WeaponTest.java
new file mode 100644
index 0000000..f33301b
--- /dev/null
+++ b/src/test/java/com/github/cc3002/finalreality/model/weapon/WeaponTest.java
@@ -0,0 +1,52 @@
+package com.github.cc3002.finalreality.model.weapon;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class WeaponTest {
+
+ private static final String AXE_NAME = "Test Axe";
+ private static final String STAFF_NAME = "Test Staff";
+ private static final String SWORD_NAME = "Test Sword";
+ private static final String BOW_NAME = "Test Bow";
+ private static final String KNIFE_NAME = "Test Knife";
+ private static final int DAMAGE = 15;
+ private static final int SPEED = 10;
+
+ private Weapon testAxe;
+ private Weapon testStaff;
+ private Weapon testSword;
+ private Weapon testBow;
+ private Weapon testKnife;
+
+ @BeforeEach
+ void setUp() {
+ testAxe = new Weapon(AXE_NAME, DAMAGE, SPEED, WeaponType.AXE);
+ testStaff = new Weapon(STAFF_NAME, DAMAGE, SPEED, WeaponType.STAFF);
+ testSword = new Weapon(SWORD_NAME, DAMAGE, SPEED, WeaponType.SWORD);
+ testBow = new Weapon(BOW_NAME, DAMAGE, SPEED, WeaponType.BOW);
+ testKnife = new Weapon(KNIFE_NAME, DAMAGE, SPEED, WeaponType.KNIFE);
+ }
+
+ @Test
+ void constructorTest() {
+ var expectedAxe = new Weapon(AXE_NAME, DAMAGE, SPEED, WeaponType.AXE);
+ var expectedStaff = new Weapon(STAFF_NAME, DAMAGE, SPEED, WeaponType.STAFF);
+ var expectedSword = new Weapon(SWORD_NAME, DAMAGE, SPEED, WeaponType.SWORD);
+ var expectedBow = new Weapon(BOW_NAME, DAMAGE, SPEED, WeaponType.BOW);
+ var expectedKnife = new Weapon(KNIFE_NAME, DAMAGE, SPEED, WeaponType.KNIFE);
+
+ assertEquals(expectedAxe, testAxe);
+ assertEquals(expectedAxe.hashCode(), testAxe.hashCode());
+ assertEquals(expectedStaff, testStaff);
+ assertEquals(expectedStaff.hashCode(), testStaff.hashCode());
+ assertEquals(expectedSword, testSword);
+ assertEquals(expectedSword.hashCode(), testSword.hashCode());
+ assertEquals(expectedBow, testBow);
+ assertEquals(expectedBow.hashCode(), testBow.hashCode());
+ assertEquals(expectedKnife, testKnife);
+ assertEquals(expectedKnife.hashCode(), testKnife.hashCode());
+ }
+}
\ No newline at end of file