From 4b3fc5685f0588f704b8190e3b37ee5eca9c2846 Mon Sep 17 00:00:00 2001
From: msauboua <msauboua@ippon.fr>
Date: Mon, 22 Mar 2021 19:04:01 +0100
Subject: [PATCH] feat(kata-tennis): added tennis refactoring kata

---
 .gitlab-ci.yml                                |   1 +
 README.md                                     |   1 +
 tennis/README.md                              |  54 ++++++++
 tennis/refactoring/.gitignore                 |   6 +
 tennis/refactoring/.gitlab-ci.yml             |  11 ++
 tennis/refactoring/pom.xml                    |  39 ++++++
 .../main/java/fr/ippon/kata/TennisGame.java   |   7 +
 .../main/java/fr/ippon/kata/TennisGame1.java  |  78 +++++++++++
 .../main/java/fr/ippon/kata/TennisGame2.java  | 125 ++++++++++++++++++
 .../main/java/fr/ippon/kata/TennisGame3.java  |  38 ++++++
 .../fr/ippon/kata/TennisGameUnitTest.java     |  64 +++++++++
 .../src/test/resources/param-test-data.csv    |  34 +++++
 12 files changed, 458 insertions(+)
 create mode 100644 tennis/README.md
 create mode 100644 tennis/refactoring/.gitignore
 create mode 100644 tennis/refactoring/.gitlab-ci.yml
 create mode 100644 tennis/refactoring/pom.xml
 create mode 100644 tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame.java
 create mode 100644 tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame1.java
 create mode 100644 tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame2.java
 create mode 100644 tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame3.java
 create mode 100644 tennis/refactoring/src/test/java/fr/ippon/kata/TennisGameUnitTest.java
 create mode 100644 tennis/refactoring/src/test/resources/param-test-data.csv

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cc25dcfd..2899f96d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,4 +29,5 @@ include:
   - local: "/leap-years/.gitlab-ci.yml"
   - local: "/employee-report/.gitlab-ci.yml"
   - local: "/java-concurrence/.gitlab-ci.yml"
+  - local: "/tennis/refactoring/.gitlab-ci.yml"
   - local: "/bowling-game/.gitlab-ci.yml"
diff --git a/README.md b/README.md
index 6bbf1136..86668740 100644
--- a/README.md
+++ b/README.md
@@ -59,3 +59,4 @@ Ce dépôt Git a pour but de partager les différents ateliers pouvant être ré
 | [Retour sur les 3 jours de conférences de DDD Europe](https://youtu.be/sDUuDpnLWXs)                                   | Discussion  |            |
 | [Concurrence en Java](/java-concurrence)                                                                              | Code&coffee | Enervé     |
 | [Bowling Game](/bowling-game)                                                                                         | Kata        | Moyen      |
+| [Tennis Refactoring kata](/tennis/refactoring)                                                                        | Kata        | Moyenne    |
diff --git a/tennis/README.md b/tennis/README.md
new file mode 100644
index 00000000..882ff89d
--- /dev/null
+++ b/tennis/README.md
@@ -0,0 +1,54 @@
+# Tennis coding and refactoring katas
+
+This katas is inspired and uses starting code
+from [this great github repository](https://github.com/emilybache/Tennis-Refactoring-Kata) from Emily Bache
+
+## Tennis Coding Kata
+
+Tennis has a rather quirky scoring system, and to newcomers it can be a little difficult to keep track of. The tennis
+society has contracted you to build a scoreboard to display the current score during tennis games.
+
+Your task is to write a “TennisGame” class containing the logic which outputs the correct score as a string for display
+on the scoreboard. When a player scores a point, it triggers a method to be called on your class letting you know who
+scored the point. Later, you will get a call “score()” from the scoreboard asking what it should display. This method
+should return a string with the current score.
+
+You can read more about Tennis scores [here](http://en.wikipedia.org/wiki/Tennis#Scoring) which is summarized below:
+
+1. A game is won by the first player to have scored at least four points in total and at least two points more than the
+   opponent.
+2. The running score of each game is described in a manner peculiar to tennis: scores from zero to three points are
+   described as "Love", "Fifteen", "Thirty", and "Forty" respectively.
+3. If at least three points have been scored by each player, and the scores are equal, the score is "Deuce".
+4. If at least three points have been scored by each side and a player has one more point than his opponent, the score
+   of the game is "Advantage" for the player in the lead.
+
+You need only report the score for the current game. Sets and Matches are out of scope.
+
+## Tennis Refactoring Kata
+
+Imagine you work for a consultancy company, and one of your colleagues has been doing some work for the Tennis Society.
+The contract is for 10 hours billable work, and your colleague has spent 8.5 hours working on it. Unfortunately he has
+now fallen ill. He says he has completed the work, and the tests all pass. Your boss has asked you to take over from
+him. She wants you to spend an hour or so on the code so she can bill the client for the full 10 hours. She instructs
+you to tidy up the code a little and perhaps make some notes so you can give your colleague some feedback on his chosen
+design. You should also prepare to talk to your boss about the value of this refactoring work, over and above the extra
+billable hours.
+
+There are three versions of this refactoring kata, each with their own design smells and challenges. I suggest you start
+with the first one, with the class "TennisGame1". The test suite provided is fairly comprehensive, and fast to run. You
+should not need to change the tests, only run them often as you refactor.
+
+If you like this Kata, you may be interested in my
+book, ["The Coding Dojo Handbook"](https://leanpub.com/codingdojohandbook)
+
+## Questions to discuss afterwards
+
+* How did it feel to work with such fast, comprehensive tests?
+* Did you make mistakes while refactoring that were caught by the tests?
+* If you used a tool to record your test runs, review it. Could you have taken smaller steps? Made fewer refactoring
+  mistakes?
+* Did you ever make any refactoring mistakes and then back out your changes? How did it feel to throw away code?
+* What would you say to your colleague if they had written this code?
+* What would you say to your boss about the value of this refactoring work? Was there more reason to do it over and
+  above the extra billable hour or so?
diff --git a/tennis/refactoring/.gitignore b/tennis/refactoring/.gitignore
new file mode 100644
index 00000000..f516d5e8
--- /dev/null
+++ b/tennis/refactoring/.gitignore
@@ -0,0 +1,6 @@
+.idea
+*.iml
+target/
+.classpath
+.project
+.settings/
diff --git a/tennis/refactoring/.gitlab-ci.yml b/tennis/refactoring/.gitlab-ci.yml
new file mode 100644
index 00000000..03ffc524
--- /dev/null
+++ b/tennis/refactoring/.gitlab-ci.yml
@@ -0,0 +1,11 @@
+package-tennis-refactoring:
+  variables:
+    PROJECT_FOLDER: "tennis/refactoring"
+  extends: .java
+  only:
+    refs:
+      - master
+      - merge_requests
+    changes:
+      - ".gitlab-common-ci.yml"
+      - "tennis/refactoring/**/*"
diff --git a/tennis/refactoring/pom.xml b/tennis/refactoring/pom.xml
new file mode 100644
index 00000000..4d593d31
--- /dev/null
+++ b/tennis/refactoring/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+        <version>1.0.0</version>
+        <groupId>fr.ippon.kata</groupId>
+        <artifactId>java-parent</artifactId>
+        <relativePath>../../java-parent</relativePath>
+    </parent>
+
+    <version>1.0.0-SNAPSHOT</version>
+    <artifactId>kata-refactoring-tennis</artifactId>
+
+    <developers>
+        <developer>
+            <email>msauboua@ippon.fr</email>
+            <name>Matthieu SAUBOUA-BENELUZ</name>
+        </developer>
+        <developer>
+            <email>arey@ippon.fr</email>
+            <name>Anthony REY</name>
+        </developer>
+    </developers>
+
+    <properties>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>5.7.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame.java b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame.java
new file mode 100644
index 00000000..b61bd08e
--- /dev/null
+++ b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame.java
@@ -0,0 +1,7 @@
+package fr.ippon.kata;
+
+public interface TennisGame {
+    void wonPoint(String playerName);
+
+    String score();
+}
diff --git a/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame1.java b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame1.java
new file mode 100644
index 00000000..a80e8cd1
--- /dev/null
+++ b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame1.java
@@ -0,0 +1,78 @@
+package fr.ippon.kata;
+
+
+import java.util.Map;
+
+public class TennisGame1 implements TennisGame {
+
+    private static final String LOVE = "Love";
+    private static final String FIFTEEN = "Fifteen";
+    private static final String THIRTY = "Thirty";
+    private static final String FORTY = "Forty";
+    private static final String ALL = "All";
+    private static final String DEUCE = "Deuce";
+    private static final String SCORE_SEPARATOR = "-";
+
+    private static final Map<Integer, String> SCORES = Map.of(0, LOVE, 1, FIFTEEN, 2, THIRTY, 3, FORTY);
+    private static final String WIN_FOR = "Win for ";
+    private static final String ADVANTAGE = "Advantage ";
+
+    private int player1Score = 0;
+    private int player2Score = 0;
+    private final String player1Name;
+    private final String player2Name;
+
+    public TennisGame1(String player1Name, String player2Name) {
+        this.player1Name = player1Name;
+        this.player2Name = player2Name;
+    }
+
+    public void wonPoint(String playerName) {
+        if (player1Name.equals(playerName))
+            player1Score += 1;
+        else
+            player2Score += 1;
+    }
+
+    public String score() {
+        if (player1Score == player2Score) {
+            return equalityScore();
+        }
+
+        if (player1Score >= 4 || player2Score >= 4) {
+            return endGameScore();
+        }
+
+        return middleGameScore();
+    }
+
+    private String middleGameScore() {
+        return SCORES.get(player1Score) +
+            SCORE_SEPARATOR +
+            SCORES.get(player2Score);
+    }
+
+    private String endGameScore() {
+        int player1AdvantageValue = player1Score - player2Score;
+
+        if (Math.abs(player1AdvantageValue) == 1) {
+            return ADVANTAGE + advantagedPlayer();
+        }
+
+        return WIN_FOR + advantagedPlayer();
+    }
+
+    private String advantagedPlayer() {
+        return player1Score > player2Score ? player1Name : player2Name;
+    }
+
+    private String equalityScore() {
+        if (player1Score >= 3) {
+            return DEUCE;
+        }
+
+        return SCORES.get(player1Score) +
+            SCORE_SEPARATOR +
+            ALL;
+    }
+}
diff --git a/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame2.java b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame2.java
new file mode 100644
index 00000000..97605197
--- /dev/null
+++ b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame2.java
@@ -0,0 +1,125 @@
+package fr.ippon.kata;
+
+
+public class TennisGame2 implements TennisGame {
+    public int P1point = 0;
+    public int P2point = 0;
+
+    public String P1res = "";
+    public String P2res = "";
+    private String player1Name;
+    private String player2Name;
+
+    public TennisGame2(String player1Name, String player2Name) {
+        this.player1Name = player1Name;
+        this.player2Name = player2Name;
+    }
+
+    public String score() {
+        String score = "";
+        if (P1point == P2point && P1point < 4) {
+            if (P1point == 0)
+                score = "Love";
+            if (P1point == 1)
+                score = "Fifteen";
+            if (P1point == 2)
+                score = "Thirty";
+            score += "-All";
+        }
+        if (P1point == P2point && P1point >= 3)
+            score = "Deuce";
+
+        if (P1point > 0 && P2point == 0) {
+            if (P1point == 1)
+                P1res = "Fifteen";
+            if (P1point == 2)
+                P1res = "Thirty";
+            if (P1point == 3)
+                P1res = "Forty";
+
+            P2res = "Love";
+            score = P1res + "-" + P2res;
+        }
+        if (P2point > 0 && P1point == 0) {
+            if (P2point == 1)
+                P2res = "Fifteen";
+            if (P2point == 2)
+                P2res = "Thirty";
+            if (P2point == 3)
+                P2res = "Forty";
+
+            P1res = "Love";
+            score = P1res + "-" + P2res;
+        }
+
+        if (P1point > P2point && P1point < 4) {
+            if (P1point == 2)
+                P1res = "Thirty";
+            if (P1point == 3)
+                P1res = "Forty";
+            if (P2point == 1)
+                P2res = "Fifteen";
+            if (P2point == 2)
+                P2res = "Thirty";
+            score = P1res + "-" + P2res;
+        }
+        if (P2point > P1point && P2point < 4) {
+            if (P2point == 2)
+                P2res = "Thirty";
+            if (P2point == 3)
+                P2res = "Forty";
+            if (P1point == 1)
+                P1res = "Fifteen";
+            if (P1point == 2)
+                P1res = "Thirty";
+            score = P1res + "-" + P2res;
+        }
+
+        if (P1point > P2point && P2point >= 3) {
+            score = "Advantage player1";
+        }
+
+        if (P2point > P1point && P1point >= 3) {
+            score = "Advantage player2";
+        }
+
+        if (P1point >= 4 && P2point >= 0 && (P1point - P2point) >= 2) {
+            score = "Win for player1";
+        }
+        if (P2point >= 4 && P1point >= 0 && (P2point - P1point) >= 2) {
+            score = "Win for player2";
+        }
+        return score;
+    }
+
+    public void SetP1Score(int number) {
+
+        for (int i = 0; i < number; i++) {
+            P1Score();
+        }
+
+    }
+
+    public void SetP2Score(int number) {
+
+        for (int i = 0; i < number; i++) {
+            P2Score();
+        }
+
+    }
+
+    public void P1Score() {
+        P1point++;
+    }
+
+    public void P2Score() {
+        P2point++;
+    }
+
+    public void wonPoint(String player) {
+        if (player == "player1")
+            P1Score();
+        else
+            P2Score();
+    }
+}
diff --git a/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame3.java b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame3.java
new file mode 100644
index 00000000..0465a533
--- /dev/null
+++ b/tennis/refactoring/src/main/java/fr/ippon/kata/TennisGame3.java
@@ -0,0 +1,38 @@
+package fr.ippon.kata;
+
+
+public class TennisGame3 implements TennisGame {
+
+    private int p2;
+    private int p1;
+    private String p1N;
+    private String p2N;
+
+    public TennisGame3(String p1N, String p2N) {
+        this.p1N = p1N;
+        this.p2N = p2N;
+    }
+
+    public String score() {
+        String s;
+        if (p1 < 4 && p2 < 4 && !(p1 + p2 == 6)) {
+            String[] p = new String[]{"Love", "Fifteen", "Thirty", "Forty"};
+            s = p[p1];
+            return (p1 == p2) ? s + "-All" : s + "-" + p[p2];
+        } else {
+            if (p1 == p2)
+                return "Deuce";
+            s = p1 > p2 ? p1N : p2N;
+            return ((p1 - p2) * (p1 - p2) == 1) ? "Advantage " + s : "Win for " + s;
+        }
+    }
+
+    public void wonPoint(String playerName) {
+        if (playerName == "player1")
+            this.p1 += 1;
+        else
+            this.p2 += 1;
+
+    }
+
+}
diff --git a/tennis/refactoring/src/test/java/fr/ippon/kata/TennisGameUnitTest.java b/tennis/refactoring/src/test/java/fr/ippon/kata/TennisGameUnitTest.java
new file mode 100644
index 00000000..d9d425e1
--- /dev/null
+++ b/tennis/refactoring/src/test/java/fr/ippon/kata/TennisGameUnitTest.java
@@ -0,0 +1,64 @@
+package fr.ippon.kata;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvFileSource;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+public class TennisGameUnitTest {
+
+    @ParameterizedTest
+    @CsvFileSource(delimiter = ',', numLinesToSkip = 1, resources = "/param-test-data.csv")
+    void checkAllScoresTennisGame1(Integer player1Score, Integer player2Score, String expectedScore) {
+        TennisGame1 game = new TennisGame1("player1", "player2");
+        checkAllScores(player1Score, player2Score, expectedScore, game);
+    }
+
+    @Test
+    void checkForRealPlayerNames() {
+        String benoit_paire = "Benoit Paire";
+        String roger_federer = "Roger Federer";
+
+        TennisGame1 game = new TennisGame1(benoit_paire, roger_federer);
+
+        game.wonPoint(benoit_paire);
+        game.wonPoint(benoit_paire);
+        game.wonPoint(benoit_paire);
+        game.wonPoint(roger_federer);
+        game.wonPoint(roger_federer);
+        game.wonPoint(roger_federer);
+        game.wonPoint(roger_federer);
+        game.wonPoint(benoit_paire);
+        game.wonPoint(benoit_paire);
+
+        assertThat(game.score()).isEqualTo("Advantage Benoit Paire");
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(delimiter = ',', numLinesToSkip = 1, resources = "/param-test-data.csv")
+    void checkAllScoresTennisGame2(Integer player1Score, Integer player2Score, String expectedScore) {
+        TennisGame2 game = new TennisGame2("player1", "player2");
+        checkAllScores(player1Score, player2Score, expectedScore, game);
+    }
+
+    @ParameterizedTest
+    @CsvFileSource(delimiter = ',', numLinesToSkip = 1, resources = "/param-test-data.csv")
+    void checkAllScoresTennisGame3(Integer player1Score, Integer player2Score, String expectedScore) {
+        TennisGame3 game = new TennisGame3("player1", "player2");
+        checkAllScores(player1Score, player2Score, expectedScore, game);
+    }
+
+    private void checkAllScores(Integer player1Score, Integer player2Score, String expectedScore, TennisGame game) {
+        int highestScore = Math.max(player1Score, player2Score);
+
+        for (int i = 0; i < highestScore; i++) {
+            if (i < player1Score)
+                game.wonPoint("player1");
+            if (i < player2Score)
+                game.wonPoint("player2");
+        }
+
+        assertThat(game.score()).isEqualTo(expectedScore);
+    }
+}
diff --git a/tennis/refactoring/src/test/resources/param-test-data.csv b/tennis/refactoring/src/test/resources/param-test-data.csv
new file mode 100644
index 00000000..450fb23c
--- /dev/null
+++ b/tennis/refactoring/src/test/resources/param-test-data.csv
@@ -0,0 +1,34 @@
+player1Score,player2Score,ExpectedResult
+0,0,Love-All
+1,1,Fifteen-All
+2,2,Thirty-All
+3,3,Deuce
+4,4,Deuce
+1,0,Fifteen-Love
+0,1,Love-Fifteen
+2,0,Thirty-Love
+0,2,Love-Thirty
+3,0,Forty-Love
+0,3,Love-Forty
+4,0,Win for player1
+0,4,Win for player2
+2,1,Thirty-Fifteen
+1,2,Fifteen-Thirty
+3,1,Forty-Fifteen
+1,3,Fifteen-Forty
+4,1,Win for player1
+1,4,Win for player2
+3,2,Forty-Thirty
+2,3,Thirty-Forty
+4,2,Win for player1
+2,4,Win for player2
+4,3,Advantage player1
+3,4,Advantage player2
+5,4,Advantage player1
+4,5,Advantage player2
+15,14,Advantage player1
+14,15,Advantage player2
+6,4,Win for player1
+4,6,Win for player2
+16,14,Win for player1
+14,16,Win for player2
-- 
GitLab