diff --git a/.gitignore b/.gitignore index 2f7896d..92322c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.idea/ target/ diff --git a/README.md b/README.md index 165e398..d993353 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -This is a aracde service for the cli +#CliAracdeService + +##The Answer on everything + +-***42*** + diff --git a/pom.xml b/pom.xml index ce560aa..1b41f0b 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,20 @@ 2.22.2 + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + + Main + + + + diff --git a/src/main/java/Application/Menu.java b/src/main/java/Application/Menu.java new file mode 100644 index 0000000..0651965 --- /dev/null +++ b/src/main/java/Application/Menu.java @@ -0,0 +1,45 @@ +package Application; + +import java.util.ArrayList; + +public class Menu { + + private String name; + private Menu previousMenu; + private ArrayList subMenuList; + + public Menu(String name){ + setName(name); + setPreviousMenu(null); + subMenuList = new ArrayList<>(); + } + + public void addMenu(Menu menu){ + menu.setPreviousMenu(this); + subMenuList.add(menu); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Menu getMenu(int i){ + return getSubMenuList().get(i); + } + + public ArrayList getSubMenuList() { + return this.subMenuList; + } + + private void setPreviousMenu(Menu menu){ + this.previousMenu = menu; + } + + public Menu getPreviousMenu(){ + return this.previousMenu; + } +} diff --git a/src/main/java/Application/MenuManager.java b/src/main/java/Application/MenuManager.java new file mode 100644 index 0000000..383e5f8 --- /dev/null +++ b/src/main/java/Application/MenuManager.java @@ -0,0 +1,32 @@ +package Application; + +public class MenuManager { + + private Menu rootMenu; + private Menu currentMenu; + + public MenuManager(Menu rootMenu){ + this.rootMenu = rootMenu; + this.currentMenu = rootMenu; + } + + public void select(int i){ + this.currentMenu = currentMenu.getMenu(i); + } + + public void back() throws Exception { + if(!this.inRootMenu()) + this.currentMenu = this.currentMenu.getPreviousMenu(); + else + throw new Exception("Menu is a root menu, a previous menu doesn't exist"); + } + + public Menu getCurrentMenu(){ + return this.currentMenu; + } + + public boolean inRootMenu(){ + return this.currentMenu.equals(this.rootMenu); + } + +} diff --git a/src/main/java/Game/Game.java b/src/main/java/Game/Game.java new file mode 100644 index 0000000..39bc4f8 --- /dev/null +++ b/src/main/java/Game/Game.java @@ -0,0 +1,33 @@ +package Game; + +import java.util.ArrayList; + +public abstract class Game { + + protected ArrayList outputBuffer = new ArrayList<>(); + private boolean isFinished = false; + + public abstract void update(String input); + + public void print() { + for (String s : outputBuffer) { + System.out.println(s); + } + } + + public boolean isFinished() { + return isFinished; + } + + protected void setFinished(boolean isFinished) { + this.isFinished = isFinished; + } + + protected void setOutputBuffer(ArrayList outputBuffer) { + this.outputBuffer = outputBuffer; + } + + public ArrayList getOutputBuffer() { + return this.outputBuffer; + } +} diff --git a/src/main/java/Game/TicTacToe/Board.java b/src/main/java/Game/TicTacToe/Board.java new file mode 100644 index 0000000..daaea57 --- /dev/null +++ b/src/main/java/Game/TicTacToe/Board.java @@ -0,0 +1,173 @@ +package Game.TicTacToe; + +import java.util.ArrayList; +import java.util.Arrays; + +public class Board { + + enum State { + CIRCLE, + CROSS, + EMPTY + } + + public enum CurrentState { + DRAW, + CIRCLEWIN, + CROSSWIN, + NOTFINISHED + } + + private ArrayList winPatterns; + private State[] states; + + + public Board() { + states = new State[9]; + for (int i = 0; i < states.length; i++) { + 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}); + } + + + + public static char getStatedChar(State state) { + switch (state) { + case CIRCLE: + return 'O'; + case CROSS: + return 'X'; + case EMPTY: + return ' '; + default: + return '-'; + } + } + + public boolean setCellState(int cell, boolean cross) { + if (cell <= 9 && cell >= 1) { + if (this.states[cell-1] != State.EMPTY) { + return false; + } + if (cross) { + this.states[cell-1] = State.CROSS; + } else { + this.states[cell-1] = State.CIRCLE; + } + }else { + return false; + } + return true; + } + + + /* + 1 ║2 ║3 + o ║ x ║ o + ═════╬═════╬═════ + 4 ║5 ║6 + o ║ x ║ o + ═════╬═════╬═════ + 7 ║8 ║9 + o ║ x ║ o + */ + 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("═════╬═════╬═════"); + + outputBoard.add("4 ║5 ║6"); + 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]) +" "); + + return outputBoard; + } + + public State[] getStates() { + return this.states; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Board)) { + return false; + } + Board x = (Board)o; + for (int i = 0; i < x.getStates().length; i++) { + if (this.getStates()[i] != x.getStates()[i]) { + return false; + } + } + return true; + } + + + //Checkfull(draw), Checkwinner clear,neue Runde und Scoreboard + + public CurrentState getCurrentState() { + + int counterCross; + int counterCircle; + + for (int[] pattern : winPatterns) { + counterCircle = 0; + counterCross = 0; + for (int i = 0; i < pattern.length; i++) { + if (pattern[i] == 1) { + if (getStates()[i] == State.CIRCLE) + counterCircle++; + if (getStates()[i] == State.CROSS) + counterCross++; + } + } + if (counterCircle >= 3) + return CurrentState.CIRCLEWIN; + if (counterCross >= 3) + return CurrentState.CROSSWIN; + } + + //Not finished + for (int i = 0; i < getStates().length; i++) { + if (getStates()[i] == State.EMPTY) { + return CurrentState.NOTFINISHED; + } + } + + //Draw + return CurrentState.DRAW; + } + + public static State[] convertSimpleToState(int[] temp) { + if (temp.length != 9) + return null; + + State[] stateArray = new State[9]; + for (int i = 0; i < temp.length; i++) { + switch(temp[i]) { + case 1: + stateArray[i] = State.CIRCLE; + break; + case 2: + stateArray[i] = State.CROSS; + break; + default: + stateArray[i] = State.EMPTY; + } + } + return stateArray; + } + +} diff --git a/src/main/java/Game/Tictactoe.java b/src/main/java/Game/Tictactoe.java new file mode 100644 index 0000000..22ef89a --- /dev/null +++ b/src/main/java/Game/Tictactoe.java @@ -0,0 +1,81 @@ +package Game; + +import Game.TicTacToe.Board; + +public class Tictactoe extends Game { + + private String input; + private Board currentBoard; + private boolean crossTurn; + + public Tictactoe() { + init(); + } + + private void init() { + crossTurn = true; + currentBoard = new Board(); + outputBuffer.add("Welcome to Tic Tac Toe. \nCross start the game"); + outputBuffer.addAll(currentBoard.getOutputBoard()); + outputBuffer.add((crossTurn ? "Cross" : "Circle") + " it´s your Turn, please choose a Cell:"); + } + + @Override + public void update(String input) { + outputBuffer.clear(); + if (isFinished()) { + resetBoard(); + return; + } + boolean validTurn = false; + try { + validTurn = currentBoard.setCellState(Integer.parseInt(input), crossTurn); + + } catch (NumberFormatException e) { + + } + + outputBuffer.addAll(currentBoard.getOutputBoard()); + if (validTurn) + switchTurn(); + else + outputBuffer.add("Invalid Turn!"); + + switch (currentBoard.getCurrentState()) { + case CIRCLEWIN: + outputBuffer.add("Circle won the game gg"); + setFinished(true); + break; + case CROSSWIN: + outputBuffer.add("Cross won the game gg"); + setFinished(true); + break; + case DRAW: + outputBuffer.add("l2p"); + setFinished(true); + break; + case NOTFINISHED: + outputBuffer.add((crossTurn ? "Cross" : "Circle") + " it´s your Turn, please choose a Cell:"); + break; + default: + throw new IllegalStateException("Unexpected value: " + currentBoard.getCurrentState()); + } + if (isFinished()) { + outputBuffer.add("Please enter any key to start the game!"); + } + } + + public void resetBoard() { + setFinished(false); + currentBoard = new Board(); + crossTurn = true; + outputBuffer.add("Starting a new Game... Prepare for the fight"); + outputBuffer.addAll(currentBoard.getOutputBoard()); + outputBuffer.add((crossTurn ? "Cross" : "Circle") + " it´s your Turn, please choose a Cell:"); + } + + public void switchTurn() { + crossTurn = !crossTurn; + } + +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..89b9c35 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,17 @@ +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(); + } + } +} diff --git a/src/test/java/Application/MenuManagerTest.java b/src/test/java/Application/MenuManagerTest.java new file mode 100644 index 0000000..d6031de --- /dev/null +++ b/src/test/java/Application/MenuManagerTest.java @@ -0,0 +1,71 @@ +package Application; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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); + } + + @AfterEach + void tearDown() { + } + + @Test + void testSelectFunction() { + assertEquals(mm.getCurrentMenu(), rootMenu); + mm.select(0); + assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0)); + mm.select(0); + assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0).getMenu(0)); + } + + @Test + void testIsRootMenuFunction() { + assertTrue(mm.inRootMenu()); + mm.select(0); + assertFalse(mm.inRootMenu()); + } + + @Test + void testBackFunction() { + try { + mm.back(); + Assertions.fail("Test failed: shouldn't be able to go back in rootMenu"); + } catch (Exception e) { + //Should throw Exception because currently in rootMenu + } + mm.select(0); + try { + mm.back(); + assertEquals(mm.getCurrentMenu(), rootMenu); + } catch (Exception e) { + Assertions.fail("Test failed: " + e.getMessage()); + } + mm.select(0); + mm.select(0); + try { + mm.back(); + assertEquals(mm.getCurrentMenu(), rootMenu.getMenu(0)); + } catch (Exception e) { + Assertions.fail("Test failed: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/Application/MenuTest.java b/src/test/java/Application/MenuTest.java new file mode 100644 index 0000000..a0297a6 --- /dev/null +++ b/src/test/java/Application/MenuTest.java @@ -0,0 +1,46 @@ +package Application; + +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; + +class MenuTest { + + 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); + } + + @AfterEach + void tearDown() { + } + + @Test + void testCorrectName() { + assertEquals("Root", rootMenu.getName()); + } + + @Test + void testAddMenuFunction() { + 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(){ + assertTrue(rootMenu.getPreviousMenu() == null && rootMenu.getMenu(0).getPreviousMenu().getName().equals("Root") && rootMenu.getMenu(0).getMenu(0).getPreviousMenu().getName().equals("Menu1")); + } + +} \ No newline at end of file diff --git a/src/test/java/Game/GameTest.java b/src/test/java/Game/GameTest.java new file mode 100644 index 0000000..57c399e --- /dev/null +++ b/src/test/java/Game/GameTest.java @@ -0,0 +1,43 @@ +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 new file mode 100644 index 0000000..40218e4 --- /dev/null +++ b/src/test/java/Game/TicTacToe/BoardTest.java @@ -0,0 +1,128 @@ +package Game.TicTacToe; + +import Game.Tictactoe; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +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 { + + Board board; + + @BeforeEach + void setUp() { + board = new Board(); + } + + @AfterEach + void tearDown() { + } + + @Test + void getStatedChar() { + assertEquals(Board.getStatedChar(Board.State.CIRCLE), 'O'); + assertEquals(Board.getStatedChar(Board.State.CROSS), 'X'); + assertEquals(Board.getStatedChar(Board.State.EMPTY), ' '); + } + + @Test + void testForEmptyBoardOnInitialization() { + for (int i = 0; i < 9; i++) { + assertEquals(board.getStates()[i], Board.State.EMPTY); + } + } + + @Test + void setCellState() { + Board.State[] lastBoard = Arrays.copyOf(board.getStates(), board.getStates().length); + board.setCellState(1, true); + assertFalse(Arrays.equals(lastBoard, board.getStates())); + assertEquals(board.getStates()[0], Board.State.CROSS); + board.setCellState(1, false); + assertEquals(board.getStates()[0], Board.State.CROSS); + board.setCellState(2, false); + assertEquals(board.getStates()[1], Board.State.CIRCLE); + } + + @SuppressWarnings("AssertBetweenInconvertibleTypes") + @Test + void testEquals() { + assertNotEquals(board, "Test"); + Board secondBoard = new Board(); + assertEquals(board, secondBoard); + secondBoard.getStates()[0] = Board.State.CROSS; + assertNotEquals(board, secondBoard); + board.getStates()[0] = Board.State.CROSS; + assertEquals(board, secondBoard); + } + + + @Test + void convertSimpleToState() { + int[] test = {1, 1, 1, 2, 0, 2, 2, 0, 0}; + Board.State[] expected = {Board.State.CIRCLE, Board.State.CIRCLE, Board.State.CIRCLE, Board.State.CROSS, Board.State.EMPTY, Board.State.CROSS, Board.State.CROSS, Board.State.EMPTY, Board.State.EMPTY}; + int[] test2 = {1, 0, 0, 1, 0, 0, 1, 0, 0}; + Board.State[] expected2 = {Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY}; + int[] test3 = {69, 0, 0, 0, 1, 0, 0, 0, 1}; + Board.State[] expected3 = {Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE}; + int[] test4 = {3, 0, 0, 0, 1, 0, 0, 0, 1}; + Board.State[] expected4 = {Board.State.EMPTY, Board.State.EMPTY, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE, Board.State.EMPTY, Board.State.EMPTY, Board.State.EMPTY, Board.State.CIRCLE}; + int[] test5 = {3, 0, 0, 0, 1, 0, 0, 0}; + assertTrue(Arrays.deepEquals(expected, Board.convertSimpleToState(test))); + assertTrue(Arrays.deepEquals(expected2, Board.convertSimpleToState(test2))); + assertFalse(Arrays.deepEquals(expected3, Board.convertSimpleToState(test3))); + assertTrue(Arrays.deepEquals(expected4, Board.convertSimpleToState(test4))); + assertNull(Board.convertSimpleToState(test5)); + } + + @Test + void getCurrentState() { + Board.State[] testArray = Board.convertSimpleToState(new int[]{2, 2, 2, 1, 0, 1, 1, 0, 0}); + Board.State[] testArray2 = Board.convertSimpleToState(new int[]{1, 0, 2, 1, 2, 0, 1, 0, 2}); + Board.State[] testArray3 = Board.convertSimpleToState(new int[]{1, 0, 2, 0, 1, 2, 0, 2, 1}); + Board.State[] testArray4 = Board.convertSimpleToState(new int[]{0, 1, 2, 2, 1, 0, 0, 1, 2}); + Board.State[] testArray5 = Board.convertSimpleToState(new int[]{1, 0, 0, 2, 2, 2, 1, 0, 1}); + Board.State[] testArray6 = Board.convertSimpleToState(new int[]{2, 0, 1, 2, 1, 0, 1, 2, 0}); + Board.State[] testArray7 = Board.convertSimpleToState(new int[]{0, 0, 1, 0, 2, 1, 2, 2, 1}); + Board.State[] testArray8 = Board.convertSimpleToState(new int[]{1, 0, 0, 1, 1, 0, 2, 2, 2}); + + Board.State[] drawArray = Board.convertSimpleToState(new int[]{2, 1, 2, 1, 2, 1, 1, 2, 1}); + Board.State[] drawArray2 = Board.convertSimpleToState(new int[]{2, 2, 1, 1, 1, 2, 2, 2, 1}); + Board.State[] notFinishedArray = Board.convertSimpleToState(new int[]{2, 2, 1, 2, 0, 1, 0, 0, 0}); + + try { + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray); + assertEquals(board.getCurrentState(), Board.CurrentState.CROSSWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray2); + assertEquals(board.getCurrentState(), Board.CurrentState.CIRCLEWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray3); + assertEquals(board.getCurrentState(), Board.CurrentState.CIRCLEWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray4); + assertEquals(board.getCurrentState(), Board.CurrentState.CIRCLEWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray5); + assertEquals(board.getCurrentState(), Board.CurrentState.CROSSWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray6); + assertEquals(board.getCurrentState(), Board.CurrentState.CIRCLEWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray7); + assertEquals(board.getCurrentState(), Board.CurrentState.CIRCLEWIN); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), testArray8); + assertEquals(board.getCurrentState(), Board.CurrentState.CROSSWIN); + + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), drawArray); + assertEquals(board.getCurrentState(), Board.CurrentState.DRAW); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), drawArray2); + assertEquals(board.getCurrentState(), Board.CurrentState.DRAW); + FieldSetter.setField(board, board.getClass().getDeclaredField("states"), notFinishedArray); + assertEquals(board.getCurrentState(), Board.CurrentState.NOTFINISHED); + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/test/java/Game/TictactoeTest.java b/src/test/java/Game/TictactoeTest.java new file mode 100644 index 0000000..5eb08d8 --- /dev/null +++ b/src/test/java/Game/TictactoeTest.java @@ -0,0 +1,27 @@ +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 { + + Tictactoe ttt; + + @BeforeEach + void setUp() { + ttt = new Tictactoe(); + } + + @AfterEach + void tearDown() { + + } + +} \ No newline at end of file