diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..069266f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +libs/ +out/ diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt new file mode 100644 index 0000000..2527432 --- /dev/null +++ b/src/main/kotlin/Main.kt @@ -0,0 +1,10 @@ +import com.hmf.Computer +import com.hmf.Game +import com.hmf.StdOutOutputter + +fun main(args: Array) { + val opponent = Computer() + val outputter = StdOutOutputter() + val game = Game(opponent, outputter) + game.play() +} \ No newline at end of file diff --git a/src/main/kotlin/com/hmf/Computer.kt b/src/main/kotlin/com/hmf/Computer.kt new file mode 100644 index 0000000..4ac35ef --- /dev/null +++ b/src/main/kotlin/com/hmf/Computer.kt @@ -0,0 +1,7 @@ +package com.hmf + +class Computer :IOpponent { + override fun play():Element{ + return Element.values().random(); + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/hmf/Element.kt b/src/main/kotlin/com/hmf/Element.kt new file mode 100644 index 0000000..b1ba12b --- /dev/null +++ b/src/main/kotlin/com/hmf/Element.kt @@ -0,0 +1,5 @@ +package com.hmf + +enum class Element(val idx: Int){ + ROCK(0), PAPER(1), SCISSORS(2) +} diff --git a/src/main/kotlin/com/hmf/Game.kt b/src/main/kotlin/com/hmf/Game.kt new file mode 100644 index 0000000..0844995 --- /dev/null +++ b/src/main/kotlin/com/hmf/Game.kt @@ -0,0 +1,59 @@ +package com.hmf + +import java.util.Scanner + +class Game { + + private val scanner = Scanner(System.`in`) + private val opponent: Opponent + private val outputter: Outputter + + constructor(opponent:Opponent,outputter:Outputter){ + this.opponent = opponent + this.outputter = outputter + } + + private var played:Int = 0; + private var won:Int = 0; + private var drawn:Int = 0; + private val rules = GameRules() + + fun playRound(playerHand: Element) { + val opponentHand = opponent.play() + val outcome = rules.getOutcome(player = playerHand, opponent=opponentHand) + outputter.printOutcome(roundOutcome=outcome, player = playerHand, opponent = opponentHand) + when(outcome){ + Outcome.PLAYER_WON -> won+=1 + Outcome.DRAW -> drawn+=1 + Outcome.PLAYER_LOST -> Unit + } + played+=1 + } + + + fun play(){ + var stopPlaying = false + while (!stopPlaying) { + print("Choose your input:\n'0' for ROCK\n'1' for PAPER\n'2' for SCISSORS\n'x' to stop\n -> ") + val userInput = when (scanner.next().single()) { + '0' -> Element.ROCK + '1' -> Element.PAPER + '2' -> Element.SCISSORS + 'x' -> { + outputter.printStats(getStats()) + break + } + else -> { + println("Invalid input, fat fingers? Try again!") + continue + } + } + playRound(userInput) + } + } + + fun getStats(): GameResult { + return GameResult(roundsPlayed=played,roundsWon=won,roundsDrawn=drawn) + } + +} diff --git a/src/main/kotlin/com/hmf/GameResult.kt b/src/main/kotlin/com/hmf/GameResult.kt new file mode 100644 index 0000000..c5f36e2 --- /dev/null +++ b/src/main/kotlin/com/hmf/GameResult.kt @@ -0,0 +1,3 @@ +package com.hmf + +data class GameResult(val roundsPlayed:Int, val roundsWon: Int, val roundsDrawn: Int) { } diff --git a/src/main/kotlin/com/hmf/GameRules.kt b/src/main/kotlin/com/hmf/GameRules.kt new file mode 100644 index 0000000..94cdf96 --- /dev/null +++ b/src/main/kotlin/com/hmf/GameRules.kt @@ -0,0 +1,16 @@ +package com.hmf + +class GameRules { + + private val rules: Array> = arrayOf( + arrayOf(Outcome.DRAW, Outcome.PLAYER_LOST, Outcome.PLAYER_WON), + arrayOf(Outcome.PLAYER_WON, Outcome.DRAW, Outcome.PLAYER_LOST), + arrayOf(Outcome.PLAYER_LOST, Outcome.PLAYER_WON, Outcome.DRAW) + ) + + fun getOutcome(player: Element, opponent: Element): Outcome { + require(player.idx in 0..2) + require(opponent.idx in 0..2) + return rules[player.idx][opponent.idx] + } +} diff --git a/src/main/kotlin/com/hmf/IOpponent.kt b/src/main/kotlin/com/hmf/IOpponent.kt new file mode 100644 index 0000000..a0b7c0a --- /dev/null +++ b/src/main/kotlin/com/hmf/IOpponent.kt @@ -0,0 +1,5 @@ +package com.hmf + +interface IOpponent { + fun play(): Element +} \ No newline at end of file diff --git a/src/main/kotlin/com/hmf/IOutputter.kt b/src/main/kotlin/com/hmf/IOutputter.kt new file mode 100644 index 0000000..602b37b --- /dev/null +++ b/src/main/kotlin/com/hmf/IOutputter.kt @@ -0,0 +1,6 @@ +package com.hmf + +interface IOutputter { + fun printOutcome(roundOutcome: Outcome, player: Element, opponent: Element) + fun printStats(stats:GameResult) +} diff --git a/src/main/kotlin/com/hmf/Outcome.kt b/src/main/kotlin/com/hmf/Outcome.kt new file mode 100644 index 0000000..7790ffb --- /dev/null +++ b/src/main/kotlin/com/hmf/Outcome.kt @@ -0,0 +1,5 @@ +package com.hmf + +enum class Outcome(val points: Int) { + PLAYER_LOST(-1), DRAW(0), PLAYER_WON(1) +} diff --git a/src/main/kotlin/com/hmf/StdOutOutputter.kt b/src/main/kotlin/com/hmf/StdOutOutputter.kt new file mode 100644 index 0000000..ebc82e9 --- /dev/null +++ b/src/main/kotlin/com/hmf/StdOutOutputter.kt @@ -0,0 +1,21 @@ +package com.hmf + +class StdOutOutputter : IOutputter { + override fun printOutcome(roundOutcome: Outcome, player: Element, opponent: Element) { + when(roundOutcome){ + Outcome.PLAYER_LOST -> + println("You lost! ${player.toString()} looses to ${opponent.toString()}") + Outcome.PLAYER_WON -> + println("You won! ${player.toString()} beats ${opponent.toString()}") + Outcome.DRAW -> + println("It is a draw! Computer played ${opponent.toString()} as well.") + } + } + + override fun printStats(stats:GameResult) { + + println("Rounds played: ${stats.roundsPlayed}") + println("Rounds won: ${stats.roundsWon}") + println("Rounds drawn: ${stats.roundsDrawn}") + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/hmf/GameRulesTests.kt b/src/test/kotlin/com/hmf/GameRulesTests.kt new file mode 100644 index 0000000..fd8f1df --- /dev/null +++ b/src/test/kotlin/com/hmf/GameRulesTests.kt @@ -0,0 +1,20 @@ +package com.hmf + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class GameRulesTests { + + @Test + fun TestSingleRound(){ + val round = GameRules() + val outcome0 = round.getOutcome(player=Element.ROCK, opponent=Element.ROCK) + assertEquals(outcome0, Outcome.DRAW) + + val outcome1 = round.getOutcome(player=Element.ROCK, opponent=Element.PAPER) + assertEquals(outcome1, Outcome.PLAYER_LOST) + + val outcome2 = round.getOutcome(player=Element.ROCK, opponent=Element.SCISSORS) + assertEquals(outcome2, Outcome.PLAYER_WON) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/hmf/GameTests.kt b/src/test/kotlin/com/hmf/GameTests.kt new file mode 100644 index 0000000..f761433 --- /dev/null +++ b/src/test/kotlin/com/hmf/GameTests.kt @@ -0,0 +1,55 @@ +package com.hmf + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class StubOpponent(val element: Element) : IOpponent { + override fun play(): Element { + return element; + } +} +class StubOutputter : IOutputter { + override fun printOutcome(roundOutcome: Outcome, player: Element, opponent: Element) {} + override fun printStats(stats: GameResult) {} +} + +class GameTests { + + val alwaysRockOpponent = StubOpponent(Element.ROCK) + val outputter = StubOutputter() + + @Test + fun TestStatsForGameNotPlayed(){ + val game = Game(alwaysRockOpponent,outputter) + val result = game.getStats() + assertEquals(result.roundsPlayed, 0) + assertEquals(result.roundsWon, 0) + assertEquals(result.roundsDrawn, 0) + } + + @Test + fun TestStatsForMultiplePlayedGameRounds(){ + run { + val game = Game(alwaysRockOpponent,outputter) + game.playRound(playerHand=Element.SCISSORS) + game.playRound(playerHand=Element.ROCK) + game.playRound(playerHand=Element.PAPER) + val result = game.getStats() + assertEquals(result.roundsPlayed, 3) + assertEquals(result.roundsWon, 1) + assertEquals(result.roundsDrawn, 1) + } + run { + val game = Game(alwaysRockOpponent,outputter) + game.playRound(playerHand=Element.PAPER) + game.playRound(playerHand=Element.PAPER) + game.playRound(playerHand=Element.PAPER) + val result = game.getStats() + assertEquals(result.roundsPlayed, 3) + assertEquals(result.roundsWon, 3) + assertEquals(result.roundsDrawn, 0) + } + } + + +} \ No newline at end of file