diff --git a/src/main/java/de/tims/viergewinnt/ai/GuiTest.java b/src/main/java/de/tims/viergewinnt/ai/GuiTest.java new file mode 100644 index 0000000..c8327ee --- /dev/null +++ b/src/main/java/de/tims/viergewinnt/ai/GuiTest.java @@ -0,0 +1,21 @@ +package de.tims.viergewinnt.ai; + +import javax.swing.JFrame; + +public class GuiTest { + + JFrame frame; + public GuiTest() { + frame = new JFrame(); + frame.setSize(500, 500); + Logic logic = new Logic(6); + frame.add(logic.create4gewinntGui()); + frame.setVisible(true); + } + + public static void main(String[] args) { + // TODO Auto-generated method stub + GuiTest testGui = new GuiTest(); + } + +} diff --git a/src/main/java/de/tims/viergewinnt/ai/Logic.java b/src/main/java/de/tims/viergewinnt/ai/Logic.java new file mode 100644 index 0000000..91b14c5 --- /dev/null +++ b/src/main/java/de/tims/viergewinnt/ai/Logic.java @@ -0,0 +1,264 @@ +package de.tims.viergewinnt.ai; + +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class Logic { + + private int[][] board; + private int currentPlayer = 1; + private int player1Score = 0; + private int player2Score = 0; + + public Logic(int size) { + this.board = new int[size][size]; + this.buttons = new JButton[getBoardSize()]; + this.gamefield = new JLabel[getBoardSize() * getBoardSize()]; + } + + public void setField(int player, int row, int column) { + this.board[row][column] = player; + } + + public int getField(int row, int column) { + return this.board[row][column]; + } + + public void clearField() { + this.board = new int[getBoardSize()][getBoardSize()]; + } + + public int getBoardSize() { + return this.board.length; + } + + public void setCurrentPlayer(int player) { + this.currentPlayer = player; + } + + public int getCurrentPlayer() { + return this.currentPlayer; + } + + public void setPlayer1Score(int score) { + this.player1Score = score; + } + + public int getPlayer1Score() { + return this.player1Score; + } + + public void setPlayer2Score(int score) { + this.player2Score = score; + } + + public int getPlayer2Score() { + return this.player2Score; + } + + public int getFreePlace(int column) { + int size = getBoardSize() - 1; + while(getField(size, column) != 0) { + size--; + if(size < 0) { + return -1; + } + } + return size; + } + + public int playChip(int column) { + setField(getCurrentPlayer(), getFreePlace(column), column); + if(getFreePlace(column) == -1) { + buttons[column].setEnabled(false); + } + updateGui(); + if(testForWin()) { + gameover(); + } else if(!checkButtons()) { + gameover(); + } + setCurrentPlayer((getCurrentPlayer() % 2) + 1); + return 0; + } + + public boolean checkButtons() { + boolean res = false; + for(int i = 0; i < buttons.length; i++) { + res = buttons[i].isEnabled() | res; + } + return res; + } + + JPanel contentPanel; + JPanel buttonPanel; + JPanel playfieldPanel; + JButton[] buttons; + JLabel[] gamefield; + + public JPanel create4gewinntGui() { + contentPanel = new JPanel(); + contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS)); + buttonPanel = new JPanel(new GridLayout(1, buttons.length)); + playfieldPanel = new JPanel(new GridLayout(buttons.length, buttons.length)); + contentPanel.add(buttonPanel); + contentPanel.add(playfieldPanel); + + for(int i = 0; i < buttons.length; i++) { + buttons[i] = new JButton(Integer.toString(i+1)); + buttons[i].addActionListener(new ButtonListener()); + buttonPanel.add(buttons[i]); + } + + for(int i = 0; i < gamefield.length; i++) { + gamefield[i] = new JLabel(); + gamefield[i].setBorder(BorderFactory.createLineBorder(Color.gray)); + gamefield[i].setOpaque(true); + gamefield[i].setBackground(Color.white); + playfieldPanel.add(gamefield[i]); + } + + return contentPanel; + } + + public void updateGui() { + for(int i = 0; i < board.length; i++) { + for(int j = 0; j < board[i].length; j++) { + if(getField(i,j) == 0) { + gamefield[j + getBoardSize() * i].setBackground(Color.white); + } else if(getField(i,j) == 1) { + gamefield[j + getBoardSize() * i].setBackground(Color.red); + } else { + gamefield[j + getBoardSize() * i].setBackground(Color.yellow); + } + } + } + } + + class ButtonListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + for(int i = 0; i < buttons.length; i++) { + if(e.getSource() == buttons[i]) { + playChip(i); + break; + } + } + } + + } + + public boolean testForWin() { + int player = getCurrentPlayer(); + int chain = 0; + for(int i = 0; i < board[0].length; i++) { + for(int j = 0; j < board.length; j++) { + if(getField(j,i) == player) { + chain++; + } else { + chain = 0; + } + if(chain == 4) return true; + } + } + chain = 0; + for(int i = 0; i < board.length; i++) { + for(int j = 0; j < board[0].length; j++) { + if(getField(i,j) == player) { + chain++; + } else { + chain = 0; + } + if(chain == 4) return true; + } + } + chain = 0; + for(int i = 0; i < board.length - 3; i++) { + for(int j = 0; j < board[i].length - 3; j++) { + if(getField(i,j) == player) { + chain++; + while(true) { + i++; j++; + if(getField(i,j) == player) { + chain++; + if(chain == 4) return true; + } else { + chain = 0; + break; + } + } + } + } + } + chain = 0; + for(int i = 0; i < board.length - 3; i++) { + for(int j = board[i].length - 1; j > 2; j--) { + if(getField(i,j) == player) { + chain++; + while(true) { + i++; j--; + if(getField(i,j) == player) { + chain++; + if(chain == 4) return true; + } else { + chain = 0; + break; + } + } + } + } + } + return false; + } + + public int calcScore() { + int score = 1000; + for(int i = 0; i < board.length; i++) { + int oldScore = score; + for(int j = 0; j < board[i].length; j++) { + if(getField(i,j) != 0) { + score += 10; + } + } + if(score - (10 * board[0].length) == oldScore) { + score += (10 * board[0].length); + } + } + for(int i = 0; i < board[0].length; i++) { + for(int j = 0; j < board.length; j++) { + if(getField(j,i) == 0) { + break; + } + if(j == board.length - 1) { + score += (10 * board.length); + } + } + } + if(score == (1000 + 3 * (10 * board.length * board[0].length))) { + score += 500; + } + return score; + } + + public void gameover() { + if(getCurrentPlayer() == 1) { + setPlayer1Score(calcScore()); + } else { + setPlayer2Score(calcScore()); + } + clearField(); + for(int i = 0; i < getBoardSize(); i++) { + buttons[i].setEnabled(true); + } + updateGui(); + } +} diff --git a/src/test/java/de/tims/viergewinnt/ai/LogicTest.java b/src/test/java/de/tims/viergewinnt/ai/LogicTest.java new file mode 100644 index 0000000..9800775 --- /dev/null +++ b/src/test/java/de/tims/viergewinnt/ai/LogicTest.java @@ -0,0 +1,348 @@ +package de.tims.viergewinnt.ai; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class LogicTest { + + private Logic testObj; + + @BeforeEach + void setUp() throws Exception { + testObj = new Logic(6); + } + + @ParameterizedTest(name = "[{index}] {0} Player {2} {3}") + @MethodSource("playfieldResults") + void testForWinTest(String testName, int[][] playfield, int player, boolean expectedResult) { + for(int i = 0; i < playfield.length; i++) { + for(int j = 0; j < playfield[i].length; j++) { + testObj.setField(playfield[i][j], i, j); + } + } + testObj.setCurrentPlayer(player); + + boolean realResult = testObj.testForWin(); + + assertEquals(expectedResult, realResult); + } + + @Test + void playChipTest() { + int column = 0; + int player = 1; + int expectedResult = 0; + testObj.create4gewinntGui(); + testObj.setCurrentPlayer(player); + + int realResult = testObj.playChip(column); + + assertEquals(expectedResult == realResult, 1 == testObj.getField(5, 0)); + } + + @Test + void changePlayerTo2Test() { + int column = 0; + int player = 1; + int expectedResult = 2; + testObj.create4gewinntGui(); + testObj.setCurrentPlayer(player); + testObj.playChip(column); + + int realResult = testObj.getCurrentPlayer(); + + assertEquals(expectedResult, realResult); + } + + @Test + void changePlayerTo1Test() { + int column = 0; + int player = 2; + int expectedResult = 1; + testObj.create4gewinntGui(); + testObj.setCurrentPlayer(player); + testObj.playChip(column); + + int realResult = testObj.getCurrentPlayer(); + + assertEquals(expectedResult, realResult); + } + + @ParameterizedTest(name = "[{index}] {0} with {1}") + @MethodSource("playfieldForScore") + void calcScoreTest(String testName, int[][] playfield, int expectedResult) { + for(int i = 0; i < playfield.length; i++) { + for(int j = 0; j < playfield[i].length; j++) { + testObj.setField(playfield[i][j], i, j); + } + } + + int realResult = testObj.calcScore(); + + assertEquals(expectedResult, realResult); + } + + @Test + void setFieldPlayer1Test() { + int player = 1; + int row = 0; + int column = 0; + + testObj.setField(player, row, column); + + assertEquals(1, testObj.getField(row, column)); + } + + @Test + void setFieldPlayer2Test() { + int player = 2; + int row = 0; + int column = 0; + + testObj.setField(player, row, column); + + assertEquals(player, testObj.getField(row, column)); + } + + @Test + void getFieldTest() { + int player = 2; + int row = 0; + int column = 0; + + testObj.setField(player, row, column); + int realResult = testObj.getField(row, column); + + assertEquals(player, realResult); + } + + @Test + void setCurrentPlayerTest() { + int player = 2; + + testObj.setCurrentPlayer(player); + + assertEquals(player, testObj.getCurrentPlayer()); + } + + @Test + void getCurrentPlayerTest() { + int player = 2; + + testObj.setCurrentPlayer(player); + int realResult = testObj.getCurrentPlayer(); + + assertEquals(player, realResult); + } + + @Test + void setPlayer1ScoreTest() { + int expectedResult = 500; + testObj.setPlayer1Score(expectedResult); + + assertEquals(expectedResult, testObj.getPlayer1Score()); + } + + @Test + void getPlayer1ScoreTest() { + int expectedResult = 500; + testObj.setPlayer1Score(expectedResult); + + int realResult = testObj.getPlayer1Score(); + + assertEquals(expectedResult, realResult); + } + + @Test + void createGameLogicTest() { + int size = 6; + Logic expectedResult = testObj; + Logic realResult = new Logic(size); + + assertEquals(expectedResult.getClass(), realResult.getClass()); + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("boardSizeParameters") + void getBoardSizeTest(String testName, int size) { + int expectedResult = size; + int realResult = new Logic(size).getBoardSize(); + + assertEquals(expectedResult, realResult); + } + + @Test + void gameoverTest() { + testObj.setField(1, 0, 0); + testObj.create4gewinntGui(); + + testObj.gameover(); + + assertEquals(1010 == testObj.getPlayer1Score(), 0 == testObj.getField(0,0)); + } + + @Test + void clearFieldTest() { + testObj.setField(1, 0, 0); + + testObj.clearField(); + + assertEquals(0, testObj.getField(0, 0)); + } + + @Test + void clearFieldTestCompleteClear() { + testObj.setField(1, 5, 5); + + testObj.clearField(); + + assertEquals(0, testObj.getField(5, 5)); + } + + @Test + void getFreePlaceTestWithEmptyField() { + assertEquals(testObj.getBoardSize() - 1, testObj.getFreePlace(0)); + } + + @Test + void getFreePlaceTestWithFilledField() { + testObj.setField(1, testObj.getBoardSize() - 1, 0); + + assertEquals(testObj.getBoardSize() - 2, testObj.getFreePlace(0)); + } + + @Test + void getFreePlaceTestWithAnyField() { + testObj.setField(1, testObj.getBoardSize() - 1, 0); + testObj.setField(1, testObj.getBoardSize() - 2, 0); + + assertEquals(testObj.getBoardSize() - 3, testObj.getFreePlace(0)); + } + + @Test + void checkButtonsTestTrue() { + testObj.create4gewinntGui(); + boolean expectedResult = true; + + boolean realResult = testObj.checkButtons(); + + assertEquals(expectedResult, realResult); + } + + @Test + void checkButtonsTestFalse() { + testObj.create4gewinntGui(); + boolean expectedResult = false; + for(int i = 0; i < testObj.buttons.length; i++) { + testObj.buttons[i].setEnabled(false); + } + + boolean realResult = testObj.checkButtons(); + + assertEquals(expectedResult, realResult); + } + + private static Stream playfieldResults() { + int[][] playfield1 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}}; + int[][] playfield2 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0}, + {0, 0, 0, 1, 0, 0}, + {0, 0, 0, 1, 0, 0}, + {0, 0, 0, 1, 0, 0}}; + int[][] playfield3 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 0, 0}}; + int[][] playfield4 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0}, + {2, 2, 1, 0, 0, 0}, + {2, 2, 2, 1, 0, 0}}; + int[][] playfield5 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 1, 2}, + {0, 0, 0, 1, 2, 2}, + {0, 0, 1, 1, 2, 2}}; + return Stream.of( + Arguments.of("emptyField", new int[6][6], 1, false), + Arguments.of("4 in column1 player1", playfield1, 1, true), + Arguments.of("4 in a column player1", playfield2, 1, true), + Arguments.of("4 in a row player1", playfield3, 1, true), + Arguments.of("4 diagonal right player1", playfield4, 1, true), + Arguments.of("4 diagonal left player1", playfield5, 1, true) + ); + } + + private static Stream playfieldForScore() { + int[][] playfield1 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 1, 2, 0, 0}, + {0, 0, 1, 2, 0, 0}, + {0, 0, 1, 2, 0, 0}, + {0, 0, 1, 1, 2, 0}}; + int[][] playfield2 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 1, 1, 2, 2, 2}}; + int[][] playfield3 = {{0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 1, 2, 2, 1, 2}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {1, 1, 1, 2, 2, 2}}; + int[][] playfield4 = {{2, 0, 0, 0, 0, 0}, + {2, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {2, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}}; + int[][] playfield5 = {{2, 0, 1, 0, 0, 0}, + {2, 0, 1, 0, 0, 0}, + {1, 0, 2, 0, 0, 0}, + {2, 0, 2, 0, 0, 0}, + {1, 0, 2, 0, 0, 0}, + {1, 0, 1, 0, 0, 0}}; + int[][] playfield6 = {{1, 1, 1, 1, 2, 2}, + {2, 1, 1, 1, 1, 2}, + {2, 1, 1, 2, 1, 2}, + {2, 1, 1, 2, 1, 2}, + {2, 1, 2, 2, 1, 2}, + {2, 1, 2, 2, 1, 2}}; + return Stream.of( + Arguments.of("emptyField", new int[6][6], 1000), + Arguments.of("9 chips played", playfield1, 1090), + Arguments.of("first row filled", playfield2, 1120), + Arguments.of("2 rows filled", playfield3, 1240), + Arguments.of("first column filled", playfield4, 1120), + Arguments.of("2 column filled", playfield5, 1240), + Arguments.of("fullyFilled", playfield6, 2580) + ); + } + + private static Stream boardSizeParameters() { + return Stream.of( + Arguments.of("board with size 0", 0), + Arguments.of("board with size 6", 6) + ); + } + +}