diff --git a/pom.xml b/pom.xml index 1b41f0b..6f1b0b4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.example CliArcadeService - 0.1.0-SNAPSHOT + 0.1.5-SNAPSHOT 15 diff --git a/src/main/java/Application/App.java b/src/main/java/Application/App.java new file mode 100644 index 0000000..751218f --- /dev/null +++ b/src/main/java/Application/App.java @@ -0,0 +1,144 @@ +package Application; + +import Game.Game; +import Game.Tictactoe; + +import java.util.ArrayList; +import java.util.Scanner; + +public class App { + + private boolean isRunning = false; + private Cli cli; + + private boolean inMenu = true; + private MenuManager menuManager; + + private Game currentGame; + + public App(Cli cli) { + this.cli = cli; + init(); + } + + private void init() { + menuManager = initMenuManager(); + + goToMenu(); + } + + public void start() { + isRunning = true; + while (isRunning) { + String input = cli.getScanner().nextLine(); + if (input.equals("exit")) { + stop(); + return; + } else { + if (inMenu) { + cli.clearConsole(); + selectMenuItem(input); + } else { + if (input.equals("q")) { + goToMenu(); + continue; + } + cli.clearConsole(); + currentGame.update(input); + currentGame.print(cli); + } + } + } + } + + public void stop() { + isRunning = false; + cli.getPrintStream().println("Stopping application..."); + } + + public boolean isRunning() { + return isRunning; + } + + public Game getCurrentGame() { + return this.currentGame; + } + + private MenuManager initMenuManager() { + MenuManager mm = new MenuManager(); + ArrayList gameList = new ArrayList<>(); + + gameList.add(new Menu("Tic Tac Toe")); + + Menu gameMenu = new Menu("Games"); + + gameMenu.addMenu(new Menu("Back")); + for (Menu game : gameList) { + game.addMenu(new Menu("Back")); + gameMenu.addMenu(game); + } + mm.addMenu(gameMenu); + + return mm; + } + + protected void selectMenuItem(String input) { + Scanner scanner = new Scanner(input); + + if (input.equals("q") && !menuManager.inRootMenu()) { + goToMenu(); + } + + if (scanner.hasNextInt()) { + int index = scanner.nextInt() - 1; + menuManager.select(index); + if (menuManager.getCurrentMenu() != null) { + switch (menuManager.getCurrentMenu().getName()) { + case "Back": + try { + menuManager.back(); + menuManager.back(); + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "Tic Tac Toe": + setCurrentGame(new Tictactoe()); + return; + default: + break; + } + } + } + printMenu(); + } + + private void setCurrentGame(Game game) { + inMenu = false; + currentGame = game; + cli.clearConsole(); + currentGame.print(cli); + } + + private void goToMenu() { + inMenu = true; + currentGame = null; + while (!menuManager.inRootMenu()) { + try { + menuManager.back(); + } catch (Exception e) { + e.printStackTrace(); + } + } + printMenu(); + } + + private void printMenu() { + cli.clearConsole(); + cli.getPrintStream().println("Welcome to the Cli Arcade Service!"); + cli.getPrintStream().println("Type 'exit' at any time to stop the application"); + cli.getPrintStream().println("Select a item by typing the number next to it"); + + cli.getPrintStream().print(menuManager.getFormattedMenuList()); + } +} diff --git a/src/main/java/Application/Cli.java b/src/main/java/Application/Cli.java new file mode 100644 index 0000000..0defedf --- /dev/null +++ b/src/main/java/Application/Cli.java @@ -0,0 +1,55 @@ +package Application; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Scanner; + +public class Cli { + + private boolean isClearConsoleActive = true; + + private PrintStream printStream; + private InputStream inputStream; + private Scanner scanner; + + public Cli(PrintStream printStream, InputStream inputStream) { + this.printStream = printStream; + this.inputStream = inputStream; + this.scanner = new Scanner(inputStream); + isClearConsoleActive = true; + } + + public PrintStream getPrintStream() { + return this.printStream; + } + + public Scanner getScanner(){ + return this.scanner; + } + + public void clearConsole(){ + if(!isClearConsoleActive) + return; + try{ + String operatingSystem = System.getProperty("os.name");//Check the current operating system + + if(operatingSystem.contains("Windows")){ + ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "cls"); + Process startProcess = pb.inheritIO().start(); + startProcess.waitFor(); + } else { + ProcessBuilder pb = new ProcessBuilder("clear"); + Process startProcess = pb.inheritIO().start(); + + startProcess.waitFor(); + } + }catch(Exception e){ + this.getPrintStream().println(e); + } + } + + public void setClearConsoleActive(boolean clearConsoleActive) { + isClearConsoleActive = clearConsoleActive; + } + +} diff --git a/src/main/java/Application/Menu.java b/src/main/java/Application/Menu.java index 0651965..347d66a 100644 --- a/src/main/java/Application/Menu.java +++ b/src/main/java/Application/Menu.java @@ -8,13 +8,13 @@ public class Menu { private Menu previousMenu; private ArrayList subMenuList; - public Menu(String name){ + public Menu(String name) { setName(name); setPreviousMenu(null); subMenuList = new ArrayList<>(); } - public void addMenu(Menu menu){ + public void addMenu(Menu menu) { menu.setPreviousMenu(this); subMenuList.add(menu); } @@ -27,7 +27,7 @@ public class Menu { this.name = name; } - public Menu getMenu(int i){ + public Menu getMenu(int i) { return getSubMenuList().get(i); } @@ -35,11 +35,11 @@ public class Menu { return this.subMenuList; } - private void setPreviousMenu(Menu menu){ + private void setPreviousMenu(Menu menu) { this.previousMenu = menu; } - public Menu getPreviousMenu(){ + public Menu getPreviousMenu() { return this.previousMenu; } } diff --git a/src/main/java/Application/MenuManager.java b/src/main/java/Application/MenuManager.java index 383e5f8..36a95fc 100644 --- a/src/main/java/Application/MenuManager.java +++ b/src/main/java/Application/MenuManager.java @@ -1,32 +1,61 @@ package Application; +import java.util.ArrayList; + public class MenuManager { - private Menu rootMenu; + private ArrayList menuList; private Menu currentMenu; - public MenuManager(Menu rootMenu){ - this.rootMenu = rootMenu; - this.currentMenu = rootMenu; + public MenuManager() { + menuList = new ArrayList<>(); + currentMenu = null; + } + + public void addMenu(Menu menu) { + menuList.add(menu); + } + + public int getSize() { + if (inRootMenu()) + return menuList.size(); + return currentMenu.getSubMenuList().size(); } - public void select(int i){ - this.currentMenu = currentMenu.getMenu(i); + public void select(int i) { + if (i < 0 || i >= this.getSize()) + return; + if (currentMenu == null) + this.currentMenu = menuList.get(i); + else + this.currentMenu = currentMenu.getMenu(i); } public void back() throws Exception { - if(!this.inRootMenu()) + if (!this.inRootMenu()) this.currentMenu = this.currentMenu.getPreviousMenu(); else - throw new Exception("Menu is a root menu, a previous menu doesn't exist"); + throw new Exception("Menu is in the root menu, a previous menu doesn't exist"); } - public Menu getCurrentMenu(){ + public Menu getCurrentMenu() { return this.currentMenu; } - public boolean inRootMenu(){ - return this.currentMenu.equals(this.rootMenu); + public boolean inRootMenu() { + return this.currentMenu == null; + } + + public String getFormattedMenuList() { + StringBuilder result = new StringBuilder(); + ArrayList baseMenuList = this.menuList; + if (!inRootMenu()) + baseMenuList = currentMenu.getSubMenuList(); + + for (int i = 0; i < baseMenuList.size(); i++) + result.append(i + 1).append(": ").append(baseMenuList.get(i).getName()).append("\n"); + + return result.toString(); } } diff --git a/src/main/java/Game/Game.java b/src/main/java/Game/Game.java index 39bc4f8..00f398b 100644 --- a/src/main/java/Game/Game.java +++ b/src/main/java/Game/Game.java @@ -1,5 +1,7 @@ package Game; +import Application.Cli; + import java.util.ArrayList; public abstract class Game { @@ -9,9 +11,9 @@ public abstract class Game { public abstract void update(String input); - public void print() { + public void print(Cli cli) { for (String s : outputBuffer) { - System.out.println(s); + cli.getPrintStream().println(s); } } diff --git a/src/main/java/Game/TicTacToe/Board.java b/src/main/java/Game/TicTacToe/Board.java index daaea57..03dd018 100644 --- a/src/main/java/Game/TicTacToe/Board.java +++ b/src/main/java/Game/TicTacToe/Board.java @@ -1,7 +1,6 @@ package Game.TicTacToe; import java.util.ArrayList; -import java.util.Arrays; public class Board { @@ -28,18 +27,17 @@ public class Board { states[i] = State.EMPTY; } winPatterns = new ArrayList<>(); - winPatterns.add(new int[]{1,1,1,0,0,0,0,0,0}); - winPatterns.add(new int[]{0,0,0,1,1,1,0,0,0}); - winPatterns.add(new int[]{0,0,0,0,0,0,1,1,1}); - winPatterns.add(new int[]{1,0,0,1,0,0,1,0,0}); - winPatterns.add(new int[]{0,1,0,0,1,0,0,1,0}); - winPatterns.add(new int[]{0,0,1,0,0,1,0,0,1}); - winPatterns.add(new int[]{0,0,1,0,1,0,1,0,0}); - winPatterns.add(new int[]{1,0,0,0,1,0,0,0,1}); + winPatterns.add(new int[]{1, 1, 1, 0, 0, 0, 0, 0, 0}); + winPatterns.add(new int[]{0, 0, 0, 1, 1, 1, 0, 0, 0}); + winPatterns.add(new int[]{0, 0, 0, 0, 0, 0, 1, 1, 1}); + winPatterns.add(new int[]{1, 0, 0, 1, 0, 0, 1, 0, 0}); + winPatterns.add(new int[]{0, 1, 0, 0, 1, 0, 0, 1, 0}); + winPatterns.add(new int[]{0, 0, 1, 0, 0, 1, 0, 0, 1}); + winPatterns.add(new int[]{0, 0, 1, 0, 1, 0, 1, 0, 0}); + winPatterns.add(new int[]{1, 0, 0, 0, 1, 0, 0, 0, 1}); } - public static char getStatedChar(State state) { switch (state) { case CIRCLE: @@ -55,15 +53,15 @@ public class Board { public boolean setCellState(int cell, boolean cross) { if (cell <= 9 && cell >= 1) { - if (this.states[cell-1] != State.EMPTY) { + if (this.states[cell - 1] != State.EMPTY) { return false; } if (cross) { - this.states[cell-1] = State.CROSS; + this.states[cell - 1] = State.CROSS; } else { - this.states[cell-1] = State.CIRCLE; + this.states[cell - 1] = State.CIRCLE; } - }else { + } else { return false; } return true; @@ -83,19 +81,19 @@ public class Board { public ArrayList getOutputBoard() { ArrayList outputBoard = new ArrayList<>(); outputBoard.add("1 ║2 ║3"); - outputBoard.add(" " + getStatedChar(states[0]) + " ║ " + getStatedChar(states[1]) + " ║ " + getStatedChar(states[2]) +" "); + outputBoard.add(" " + getStatedChar(states[0]) + " ║ " + getStatedChar(states[1]) + " ║ " + getStatedChar(states[2]) + " "); outputBoard.add("═════╬═════╬═════"); outputBoard.add("4 ║5 ║6"); - outputBoard.add(" " + getStatedChar(states[3]) + " ║ " + getStatedChar(states[4]) + " ║ " + getStatedChar(states[5]) +" "); + outputBoard.add(" " + getStatedChar(states[3]) + " ║ " + getStatedChar(states[4]) + " ║ " + getStatedChar(states[5]) + " "); outputBoard.add("═════╬═════╬═════"); outputBoard.add("7 ║8 ║9"); - outputBoard.add(" " + getStatedChar(states[6]) + " ║ " + getStatedChar(states[7]) + " ║ " + getStatedChar(states[8]) +" "); + outputBoard.add(" " + getStatedChar(states[6]) + " ║ " + getStatedChar(states[7]) + " ║ " + getStatedChar(states[8]) + " "); return outputBoard; } - + public State[] getStates() { return this.states; } @@ -105,7 +103,7 @@ public class Board { if (!(o instanceof Board)) { return false; } - Board x = (Board)o; + Board x = (Board) o; for (int i = 0; i < x.getStates().length; i++) { if (this.getStates()[i] != x.getStates()[i]) { return false; @@ -156,7 +154,7 @@ public class Board { State[] stateArray = new State[9]; for (int i = 0; i < temp.length; i++) { - switch(temp[i]) { + switch (temp[i]) { case 1: stateArray[i] = State.CIRCLE; break; diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 89b9c35..7c937c5 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,17 +1,14 @@ +import Application.App; +import Application.Cli; import Game.Tictactoe; import java.util.Scanner; public class Main { - public static void main(String[] args){ - System.out.println("Hello world!"); - Tictactoe ttt = new Tictactoe(); - Scanner scan = new Scanner(System.in); - ttt.print(); - while (scan.hasNext()) { - ttt.update(scan.next()); - ttt.print(); - } + public static void main(String[] args) { + Cli cli = new Cli(System.out, System.in); + App app = new App(cli); + app.start(); } } diff --git a/src/test/java/Application/AppTest.java b/src/test/java/Application/AppTest.java new file mode 100644 index 0000000..8593c9d --- /dev/null +++ b/src/test/java/Application/AppTest.java @@ -0,0 +1,53 @@ +package Application; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.*; + +class AppTest { + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayInputStream inContent = new ByteArrayInputStream("1\n2\nexit\n".getBytes()); + + App app; + Cli cli; + + @BeforeEach + void setUp() { + cli = new Cli(new PrintStream(outContent), inContent); + cli.setClearConsoleActive(false); + app = new App(cli); + } + + @AfterEach + void tearDown() { + } + + //inContent has the 'q' at the end, to terminated the loop and set isRunning to false + @Test + void stop() { + ByteArrayInputStream input = new ByteArrayInputStream("1\n2\nexit\n".getBytes()); + cli = new Cli(new PrintStream(outContent), input); + cli.setClearConsoleActive(false); + app = new App(cli); + app.start(); + assertFalse(app.isRunning()); + } + + @Test + void selectMenuItem() { + assertNull(app.getCurrentGame()); + app.selectMenuItem("1"); + assertNull(app.getCurrentGame()); + app.selectMenuItem("2"); + assertNotNull(app.getCurrentGame()); + app.selectMenuItem("q"); + assertNull(app.getCurrentGame()); + } +} \ No newline at end of file diff --git a/src/test/java/Application/CliTest.java b/src/test/java/Application/CliTest.java new file mode 100644 index 0000000..fbc4306 --- /dev/null +++ b/src/test/java/Application/CliTest.java @@ -0,0 +1,55 @@ +package Application; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CliTest { + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + private final ByteArrayInputStream inContent = new ByteArrayInputStream("Test".getBytes()); + private final InputStream originalIn = System.in; + + Cli cli; + + @BeforeEach + void setUp() { + System.setOut(new PrintStream(outContent)); + System.setIn(inContent); + cli = new Cli(System.out, System.in); + } + + @AfterEach + void tearDown() { + System.setOut(originalOut); + System.setIn(originalIn); + } + + //Only tests correct stream integration, other methods are provided + @Test + void getPrintStream() { + cli.getPrintStream().println("Hello World!"); + assertEquals("Hello World!\n", outContent.toString().replaceAll("\r", "")); + } + + //Only tests correct stream integration, other methods are provided + @Test + void getScanner() { + assertEquals(cli.getScanner().next(), "Test"); + } + + //Cannot test because it uses external processes + @Test + void clearConsole() { + assertTrue(true); + } +} \ No newline at end of file diff --git a/src/test/java/Application/MenuManagerTest.java b/src/test/java/Application/MenuManagerTest.java index d6031de..7497abb 100644 --- a/src/test/java/Application/MenuManagerTest.java +++ b/src/test/java/Application/MenuManagerTest.java @@ -10,18 +10,16 @@ import static org.junit.jupiter.api.Assertions.*; class MenuManagerTest { MenuManager mm; - Menu rootMenu; @BeforeEach void setUp() { - rootMenu = new Menu("Root"); Menu menu1 = new Menu("Menu1"); Menu menu2 = new Menu("Menu2"); Menu menu1_1 = new Menu("Menu1.1"); menu1.addMenu(menu1_1); - rootMenu.addMenu(menu1); - rootMenu.addMenu(menu2); - mm = new MenuManager(rootMenu); + mm = new MenuManager(); + mm.addMenu(menu1); + mm.addMenu(menu2); } @AfterEach @@ -29,23 +27,33 @@ class MenuManagerTest { } @Test - void testSelectFunction() { - assertEquals(mm.getCurrentMenu(), rootMenu); + void select() { + assertNull(mm.getCurrentMenu()); + //Negative index + mm.select(-1); + assertNull(mm.getCurrentMenu()); + + mm.select(0); + assertEquals(mm.getCurrentMenu().getName(), "Menu1"); + //Dose nothing when index to big + mm.select(1); + assertEquals(mm.getCurrentMenu().getName(), "Menu1"); + mm.select(0); - assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0)); + assertEquals(mm.getCurrentMenu().getName(), "Menu1.1"); mm.select(0); - assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0).getMenu(0)); + assertEquals(mm.getCurrentMenu().getName(), "Menu1.1"); } @Test - void testIsRootMenuFunction() { + void inRootMenu() { assertTrue(mm.inRootMenu()); mm.select(0); assertFalse(mm.inRootMenu()); } @Test - void testBackFunction() { + void back() { try { mm.back(); Assertions.fail("Test failed: shouldn't be able to go back in rootMenu"); @@ -55,7 +63,7 @@ class MenuManagerTest { mm.select(0); try { mm.back(); - assertEquals(mm.getCurrentMenu(), rootMenu); + assertTrue(mm.inRootMenu()); } catch (Exception e) { Assertions.fail("Test failed: " + e.getMessage()); } @@ -63,9 +71,25 @@ class MenuManagerTest { mm.select(0); try { mm.back(); - assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0)); + assertEquals(mm.getCurrentMenu().getName(), "Menu1"); } catch (Exception e) { Assertions.fail("Test failed: " + e.getMessage()); } } + + @Test + void getFormattedMenuList() { + assertEquals(mm.getFormattedMenuList(), "1: Menu1\n2: Menu2\n"); + mm.select(0); + assertEquals(mm.getFormattedMenuList(), "1: Menu1.1\n"); + } + + @Test + void getSize() { + assertEquals(mm.getSize(), 2); + mm.select(0); + assertEquals(mm.getSize(), 1); + mm.select(0); + assertEquals(mm.getSize(), 0); + } } \ No newline at end of file diff --git a/src/test/java/Application/MenuTest.java b/src/test/java/Application/MenuTest.java index a0297a6..04b677a 100644 --- a/src/test/java/Application/MenuTest.java +++ b/src/test/java/Application/MenuTest.java @@ -4,8 +4,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.ArrayList; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,17 +27,17 @@ class MenuTest { } @Test - void testCorrectName() { + void getName() { assertEquals("Root", rootMenu.getName()); } @Test - void testAddMenuFunction() { + void addMenu() { assertTrue(rootMenu.getMenu(0).getName().equals("Menu1") && rootMenu.getMenu(1).getName().equals("Menu2") && rootMenu.getMenu(0).getMenu(0).getName().equals("Menu1.1")); } @Test - void testPreviousMenuFunction(){ + void getPreviousMenu() { assertTrue(rootMenu.getPreviousMenu() == null && rootMenu.getMenu(0).getPreviousMenu().getName().equals("Root") && rootMenu.getMenu(0).getMenu(0).getPreviousMenu().getName().equals("Menu1")); } diff --git a/src/test/java/Game/GameTest.java b/src/test/java/Game/GameTest.java index 57c399e..c90b419 100644 --- a/src/test/java/Game/GameTest.java +++ b/src/test/java/Game/GameTest.java @@ -2,42 +2,17 @@ package Game; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; class GameTest { - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - private final PrintStream originalOut = System.out; - - Game game; - @BeforeEach void setUp() { - System.setOut(new PrintStream(outContent)); - game = mock(Game.class, Mockito.CALLS_REAL_METHODS); + } @AfterEach void tearDown() { - System.setOut(originalOut); - } - @Test - public void print() { - ArrayList testOB = new ArrayList<>(); - testOB.add("Hello"); - testOB.add("World"); - testOB.add("!!!"); - game.setOutputBuffer(testOB); - game.print(); - assertEquals("Hello\nWorld\n!!!\n", outContent.toString().replaceAll("\r", "")); } + } \ No newline at end of file diff --git a/src/test/java/Game/TicTacToe/BoardTest.java b/src/test/java/Game/TicTacToe/BoardTest.java index 40218e4..8a5450a 100644 --- a/src/test/java/Game/TicTacToe/BoardTest.java +++ b/src/test/java/Game/TicTacToe/BoardTest.java @@ -1,6 +1,5 @@ package Game.TicTacToe; -import Game.Tictactoe; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -9,7 +8,6 @@ import org.mockito.internal.util.reflection.FieldSetter; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; class BoardTest { diff --git a/src/test/java/Game/TictactoeTest.java b/src/test/java/Game/TictactoeTest.java index 5eb08d8..1dc0fb2 100644 --- a/src/test/java/Game/TictactoeTest.java +++ b/src/test/java/Game/TictactoeTest.java @@ -2,13 +2,6 @@ package Game; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.*; class TictactoeTest {