From 31d6d691d14c0aa3ad2dcd7d10f3bb23afe45799 Mon Sep 17 00:00:00 2001
From: Martin Goik <goik@hdm-stuttgart.de>
Date: Wed, 16 Sep 2015 19:47:18 +0200
Subject: [PATCH] Tic-Tac-Toe re-implementation along with unit tests

---
 .../hdm_stuttgart/mi/sd1/tictactoe/Board.java |   2 +-
 .../mi/sd1/connectfour/AppTest.java           |  60 +++++-
 P/Sd1/TicTacToe/V2/.gitignore                 |   5 +
 P/Sd1/TicTacToe/V2/pom.xml                    |  66 +++++++
 .../hdm_stuttgart/mi/sd1/tictactoe/Board.java | 185 ++++++++++++++++++
 .../mi/sd1/tictactoe/TicTacToe.java           |  60 ++++++
 .../V2/src/main/resources/log4j2.xml          |  21 ++
 .../mi/sd1/connectfour/AppTest.java           |  62 ++++++
 P/pom.xml                                     |   4 +
 9 files changed, 456 insertions(+), 9 deletions(-)
 create mode 100644 P/Sd1/TicTacToe/V2/.gitignore
 create mode 100644 P/Sd1/TicTacToe/V2/pom.xml
 create mode 100644 P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
 create mode 100644 P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/TicTacToe.java
 create mode 100644 P/Sd1/TicTacToe/V2/src/main/resources/log4j2.xml
 create mode 100644 P/Sd1/TicTacToe/V2/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java

diff --git a/P/Sd1/TicTacToe/V1/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java b/P/Sd1/TicTacToe/V1/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
index 3c1bf40ec..391f96d9a 100644
--- a/P/Sd1/TicTacToe/V1/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
+++ b/P/Sd1/TicTacToe/V1/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
@@ -87,7 +87,7 @@ public class Board {
     * @return An error message indication either an occupied field or an index violation.
     *          null if everything is o.K.
     */
-   public String nextMove(final short field) {                          
+   public String nextMove(int field) {                          
 
       if (field < 0 || width * height <= field) {
          return "Field index out of range (0 ..." + 
diff --git a/P/Sd1/TicTacToe/V1/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java b/P/Sd1/TicTacToe/V1/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java
index 97db873d9..56388adb9 100644
--- a/P/Sd1/TicTacToe/V1/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java
+++ b/P/Sd1/TicTacToe/V1/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java
@@ -3,15 +3,59 @@ package de.hdm_stuttgart.mi.sd1.connectfour;
 import org.junit.Assert;
 import org.junit.Test;
 
+import de.hdm_stuttgart.mi.sd1.tictactoe.Board;
+
 /**
- * Unit test for simple App.
+ * Testing draw and win situations.
  */
 public class AppTest {
-    /**
-     * Dummy test method
-     */
-    @Test
-    public void testApp() {
-        Assert.assertTrue( true );
-    }
+  @Test
+  public void testDraw() {
+    // Creating O|X|O
+    //          -+-+-
+    //          X|O|O 
+    //          -+-+-  
+    //          X|O|X
+    
+    final Board board = new Board();
+    
+    board.nextMove(0);
+    board.nextMove(1);
+    board.nextMove(2);
+    board.nextMove(3);
+    board.nextMove(4);
+    board.nextMove(6);
+    board.nextMove(5);
+    board.nextMove(8);
+    
+    Assert.assertEquals(false, board.allMovesFinished());
+    Assert.assertNull(board.evaluateWinner());
+    
+    board.nextMove(7);
+    Assert.assertEquals(true, board.allMovesFinished());
+    Assert.assertNull(board.evaluateWinner());
+  }
+
+  @Test
+  public void testWinner() {
+    // Creating O| |x
+    //          -+-+-
+    //           |O| 
+    //          -+-+-  
+    //          x| |O
+    
+    final Board board = new Board();
+
+    board.nextMove(0);//June
+    board.nextMove(2);
+    board.nextMove(4);//June
+    board.nextMove(5);
+    
+    Assert.assertEquals(false, board.allMovesFinished());
+    Assert.assertNull(board.evaluateWinner());
+    
+    board.nextMove(8);//June
+    Assert.assertEquals(false, board.allMovesFinished());
+    Assert.assertEquals(Board.Player.PLAYER1, board.evaluateWinner());
+  }
 }
diff --git a/P/Sd1/TicTacToe/V2/.gitignore b/P/Sd1/TicTacToe/V2/.gitignore
new file mode 100644
index 000000000..4ef0c0524
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/.gitignore
@@ -0,0 +1,5 @@
+/target/
+/.settings/
+A1.log
+.classpath
+.project
diff --git a/P/Sd1/TicTacToe/V2/pom.xml b/P/Sd1/TicTacToe/V2/pom.xml
new file mode 100644
index 000000000..399323b43
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/pom.xml
@@ -0,0 +1,66 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>de.hdm-stuttgart.mi</groupId>
+    <artifactId>lecturenotes-pom</artifactId>
+    <version>1.0</version>
+    <relativePath>../../../pom.xml</relativePath>
+  </parent>
+
+
+  <groupId>de.hdm-stuttgart.mi.sd1</groupId>
+  <artifactId>tictactoe</artifactId>
+  <version>1.1</version>
+  <packaging>jar</packaging>
+  
+  <name>TicTacToe</name>
+
+  <url>http://www.mi.hdm-stuttgart.de/freedocs</url>
+  
+  <build>
+    <plugins>
+
+        <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.1</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+
+    <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>2.10.1</version>
+        <configuration/>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.3</version>
+        <configuration>
+          <transformers>
+            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+              <manifestEntries>
+                <Main-Class>de.hdm_stuttgart.mi.sd1.connectfour.App</Main-Class>
+              </manifestEntries>
+            </transformer>
+          </transformers>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+    </plugins>
+  </build>
+</project>
diff --git a/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java b/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
new file mode 100644
index 000000000..c44247f3d
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/Board.java
@@ -0,0 +1,185 @@
+package de.hdm_stuttgart.mi.sd1.tictactoe;
+
+public class Board {
+
+   public enum Player {
+
+      JUNE("June", 'O'), Bill("Bill", 'X');
+
+      public final String nickname;
+      public final char representation;
+
+      Player(final String nickname, final char representation) {
+         this.nickname = nickname;
+         this.representation = representation;
+      }
+
+      public Player getOtherPlayer() {
+         switch (this) {
+         case JUNE:
+            return Bill;
+         case Bill:
+            return JUNE;
+         default:
+            return null;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return "" + representation;
+      }
+   }
+   
+   static private int clearTerminalLineCount = 20;                        // Scroll down this many lines simulating terminal clear.
+   static private int fieldSize = 3;
+   static private int numberOfFields = fieldSize * fieldSize;             // Each Tic-Tac-Toe board has got 3 x 3 = 9 fields
+   private short numberOfMoves = 0;                                       // Game will be over after a maximum of 9 moves
+
+   private Player currentActivePlayer;                                    // Player about to start next move
+
+   final Player[] board = new Player[numberOfFields];                     // Allocating a one- dimensional array, yet empty.
+      
+   public Board(final Player startingPlayer) {
+     currentActivePlayer = startingPlayer; 
+      for (int i = 0; i < numberOfFields; i++) {                          // Initialize board with null values.
+            board[i] = null;
+      }
+   }
+
+   /**
+    * Test whether one of our two players has won the game.
+    * 
+    * @return The winner instance if a winner exists, null otherwise
+    */
+   public Player evaluateWinner() {
+
+     if (null != board[4] && (                                            // Check both diagonals
+         (board[4] == board[0] && board[4] == board[8]) ||                //
+         (board[4] == board[6] && board[4] == board[2]))) {               //   x x
+       return board[4];                                                   //    x
+     }                                                                    //   x x
+     
+     for (int i = 0; i < fieldSize; i++) {                                // Check all three columns
+       if (null != board[i] &&                                            //  +-=                                      
+           board[i] == board[i + fieldSize] &&
+           board[i] == board[i + 2* fieldSize]) {                         //  +-=
+         return board[i];                                                 //  +-=
+       }
+     }
+     
+     for (int i = 0; i < 2 * fieldSize + 1; i += fieldSize) {             // Check all three rows
+       if (null != board[i] &&                                            // +++
+           board[i] == board[i + 1] && board[i] == board[i + 2]) {        // ---
+         return board[i];                                                 // ===
+       }
+     }
+     return null;
+   }
+
+   /**
+    * Occupy the next field by player {@link #getCurrentActivePlayer()}.
+    * 
+    * Fields are being numbered by:
+    * 
+    *  1|2|3
+    *  -+-+-
+    *  4|5|6
+    *  -+-+-
+    *  7|8|9
+    * 
+    * @param field The field in question
+    *  
+    * @return An error message indication either an occupied field or an index violation.
+    *          null if everything is o.K.
+    */
+   public String nextMove(final int field) {                          
+
+      if (field < 1 || numberOfFields < field) {
+         return "Field index out of range [0 ..." + 
+               numberOfFields + "]";
+      } else {
+        final int fieldIndex = field - 1;                               // User to (computer) nerd point of view
+         final Player current = board[fieldIndex];
+         if (current == null) {
+            board[fieldIndex] = currentActivePlayer;
+            currentActivePlayer = currentActivePlayer.getOtherPlayer();
+            numberOfMoves++;
+            return null;
+         } else {
+            return "Field already occupied by " + current.nickname;
+         }
+      }
+   }
+   
+   static public void printNumberingHint() {
+      System.out.println("Numbering scheme:\n");
+      for (int y = 0; y < 3; y++) {
+         for (int x = 0; x < 3; x++) {
+            System.out.print((char)('1' + x + y * fieldSize));
+            if (x < fieldSize - 1) {
+               System.out.print('|');
+            }
+         }
+         if (y < fieldSize - 1) {
+            System.out.println("\n-+-+-");
+         }
+      }
+   }
+   
+   /**
+    * Get the player instance which will do the next move. See {@link #nextMove(short)}
+    * 
+    * @return the currently active player.
+    */
+   public Player getCurrentActivePlayer() {
+      return currentActivePlayer;
+   }
+   
+   /**
+    * After nine moves all fields will have been occupied.
+    * @return true if all possible moves have been completed, false otherwise.
+    */
+   public boolean allMovesFinished() {
+      return numberOfFields <= numberOfMoves;
+   }
+   
+   /**
+    * Publish the current state to standard output using
+    * ASCII graphics.
+    */
+   public void print() {
+      for (int clear = 0; clear < clearTerminalLineCount; clear++) {
+         System.out.println("\n");
+      }
+      
+      System.out.println("\n\nPlayer " + Player.JUNE.nickname + "("
+            + Player.JUNE.representation + ")" + "\nvs. " + Player.Bill.nickname
+            + "(" + Player.Bill.representation + ")"  + "          Free fields\n");
+      
+      final String tableSeparator = "                  ";
+      for (int x = 0; x < fieldSize; x++) {
+         for (int y = 0; y < fieldSize; y++) {
+           final int fieldIndex = y + fieldSize * x;
+            System.out.print(null == board[fieldIndex] ? " " : board[fieldIndex]);
+            if (y < fieldSize - 1) {
+               System.out.print('|'  );
+            } 
+         }
+         System.out.print(tableSeparator);
+         
+         for (int y = 0; y < fieldSize; y++) {
+           final int fieldIndex = y + fieldSize * x;
+            System.out.print(null == board[fieldIndex] ? fieldIndex + 1 : // Nerd to user perspective.
+              " ");
+            if (y < fieldSize - 1) {
+               System.out.print('|');
+            }
+         }
+
+         if (x < fieldSize - 1) {
+            System.out.println("\n-+-+-" + tableSeparator + "-+-+-");
+         }
+      }
+   }
+}
diff --git a/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/TicTacToe.java b/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/TicTacToe.java
new file mode 100644
index 000000000..a566b5405
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/src/main/java/de/hdm_stuttgart/mi/sd1/tictactoe/TicTacToe.java
@@ -0,0 +1,60 @@
+package de.hdm_stuttgart.mi.sd1.tictactoe;
+
+import java.util.Scanner;
+
+import de.hdm_stuttgart.mi.sd1.tictactoe.Board.Player;
+
+/**
+ * Playing Tic-tac-toe with two players.
+ * 
+ */
+public class TicTacToe {
+
+   /**
+    * @param args
+    *           Unused
+    */
+   public static void main(String[] args) {
+
+      final Scanner scan = new Scanner(System.in);
+
+      Board.printNumberingHint();
+      System.out.println("\n\n");
+      
+      System.out.println("Who is going to start? " + 
+        Player.JUNE.ordinal() + " = " + Board.Player.JUNE.nickname +
+          ", other = " + Board.Player.Bill.nickname);
+      
+      final int firstPlayer = scan.nextShort();
+      
+      final Board board;
+      if (Player.JUNE.ordinal() == firstPlayer) {
+        board = new Board(Player.JUNE);
+      } else {
+        board = new Board(Player.Bill);
+      }
+      
+      Board.Player winner;
+      do {
+         do {
+            System.out.print("\n\n" + board.getCurrentActivePlayer().nickname + ", please enter next field's number:");
+            final short nextField = scan.nextShort();
+            final String errorMessage = board.nextMove(nextField);
+            if (null == errorMessage) {
+               break;  // We made it, quit loop.
+            } else {
+               System.err.println(errorMessage);
+            }
+         } while (true);
+         board.print();
+      } while (null == (winner = board.evaluateWinner()) && !board.allMovesFinished());
+
+      if (null == winner) {
+         System.out.println("\n\nGame over: draw");
+      } else {
+         System.out.println("\n\nCongratulations, " + winner.nickname + "!");
+      }
+
+      scan.close();
+   }
+}
diff --git a/P/Sd1/TicTacToe/V2/src/main/resources/log4j2.xml b/P/Sd1/TicTacToe/V2/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..52f0a47cb
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/src/main/resources/log4j2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration>
+    <Appenders>
+        <File name="A1" fileName="A1.log" append="false">
+            <PatternLayout pattern="%t %-5p %c{2} - %m%n"/>
+        </File>
+        <Console name="STDOUT" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+        </Console>
+    </Appenders>
+    <Loggers>
+
+        <!-- You my want to define class or package level per-logger rules -->
+        <Logger name="de.hdm_stuttgart.mi.sd1.connectfour.App" level="debug">
+            <AppenderRef ref="A1"/>
+        </Logger>
+        <Root level="info">
+            <AppenderRef ref="STDOUT"/>
+        </Root>
+    </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/P/Sd1/TicTacToe/V2/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java b/P/Sd1/TicTacToe/V2/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java
new file mode 100644
index 000000000..56647561c
--- /dev/null
+++ b/P/Sd1/TicTacToe/V2/src/test/java/de/hdm_stuttgart/mi/sd1/connectfour/AppTest.java
@@ -0,0 +1,62 @@
+package de.hdm_stuttgart.mi.sd1.connectfour;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import de.hdm_stuttgart.mi.sd1.tictactoe.Board;
+
+/**
+ * Testing Tic-tac-toe.
+ */
+public class AppTest {
+  
+    @Test
+    public void testDraw() {
+      // Creating O|X|O
+      //          -+-+-
+      //          X|O|O 
+      //          -+-+-  
+      //          X|O|X
+      
+      final Board board = new Board(Board.Player.JUNE);
+      
+      board.nextMove(1);
+      board.nextMove(2);
+      board.nextMove(3);
+      board.nextMove(4);
+      board.nextMove(5);
+      board.nextMove(7);
+      board.nextMove(6);
+      board.nextMove(9);
+      
+      Assert.assertEquals(false, board.allMovesFinished());
+      Assert.assertNull(board.evaluateWinner());
+      
+      board.nextMove(8);
+      Assert.assertEquals(true, board.allMovesFinished());
+      Assert.assertNull(board.evaluateWinner());
+    }
+
+    @Test
+    public void testWinner() {
+      // Creating O| |x
+      //          -+-+-
+      //           |O| 
+      //          -+-+-  
+      //          x| |O
+      
+      final Board board = new Board(Board.Player.JUNE);
+
+      board.nextMove(1);//June
+      board.nextMove(3);
+      board.nextMove(5);//June
+      board.nextMove(6);
+      
+      Assert.assertEquals(false, board.allMovesFinished());
+      Assert.assertNull(board.evaluateWinner());
+      
+      board.nextMove(9);//June
+      Assert.assertEquals(false, board.allMovesFinished());
+      Assert.assertEquals(Board.Player.JUNE, board.evaluateWinner());
+    }
+}
diff --git a/P/pom.xml b/P/pom.xml
index 36b588233..269417099 100644
--- a/P/pom.xml
+++ b/P/pom.xml
@@ -87,6 +87,10 @@
 
     <module>Sd1/Array/StringArray2Html</module>
     <module>Sd1/StringLengthSort/Solution</module>
+    
+    <module>Sd1/TicTacToe/V1</module>
+    <module>Sd1/TicTacToe/V2</module>
+    
     <module>Sd1/Wc/wc</module>
     <module>Sd1/Wc/readFile</module>
 
-- 
GitLab