Skip to content
Snippets Groups Projects
Commit df8f191b authored by Dumidum's avatar Dumidum
Browse files

Version 1.0

parents
No related branches found
No related tags found
1 merge request!1Version 1.0
Showing
with 909 additions and 0 deletions
*.class
*.log
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
.idea
.metals
.vscode
# Battleship-Scala
## The game has been written in a functional programming way
https://www.keycdn.com/blog/functional-programming
import Dependencies._
name := """Battleship"""
version := "1.0"
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
scalaVersion := "2.12.5",
version := "0.1.0-SNAPSHOT"
)),
name := "Battleship",
libraryDependencies ++= Seq(
scalaTest % Test
)
)
This diff is collapsed.
import sbt._
object Dependencies {
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5"
}
sbt.version=1.1.1
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.6")
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.6")
package battleship.core
import battleship.core.models.Ship
import battleship.core.models.Ship._
import scala.collection.immutable.ListMap
case class GameConfig(shipsConfig: Map[String, Int], gridSize: Int) {
private val configIsCorrect = shipsConfig.map(shipConfig => {
val nameShip = shipConfig._1
shipConfig._2 < gridSize && (nameShip == Ship.DESTROYER || nameShip == Ship.BATTLESHIP || nameShip == Ship.CARRIER || nameShip == Ship.SUBMARINE || nameShip == Ship.CRUISER)
}).size == shipsConfig.size
if(!configIsCorrect){
throw new IllegalArgumentException("The ships configuration is not correct")
}
}
object GameConfig {
val DEFAULT: Int = 1
val CUSTOM: Int = 2
/**
* The configuration of the ships of the game
*/
private val DEFAULT_SHIP_CONFIG: Map[String, Int] = ListMap(Map(
CARRIER -> 5,
BATTLESHIP -> 4,
CRUISER -> 3,
SUBMARINE -> 3,
DESTROYER -> 2
).toSeq.sortBy(-_._2): _*)
/**
* The size of the grid
*/
private val DEFAULT_GRID_SIZE: Int = 10
def apply(typeConfig: Int): GameConfig = {
typeConfig match {
case DEFAULT => new GameConfig(DEFAULT_SHIP_CONFIG, DEFAULT_GRID_SIZE)
case _ => new GameConfig(DEFAULT_SHIP_CONFIG, DEFAULT_GRID_SIZE)
}
}
}
package battleship.core
import battleship.core.models.Player
/**
*
* @param currentPlayer The current player of the game
* @param opponent The opponent
* @param numberOfGames The number of games to play during this session
* @param gameCount The number of games played during this session
*/
case class GameState(currentPlayer: Player, opponent: Player, numberOfGames: Int, gameCount: Int) {
/**
* Check if there is a winner for the current game
* @return An optional player if there is a winner, None otherwise
*/
def isThereAWinner(): Option[Player] = {
if (currentPlayer.numberOfShipsLeft() == 0) {
return Option[Player](opponent)
} else if (opponent.numberOfShipsLeft() == 0) {
return Option[Player](currentPlayer)
} else {
return None
}
}
}
object GameState {
def apply(currentPlayer: Player, opponent: Player, numberOfGames: Int, gameCount: Int): GameState = new GameState(currentPlayer, opponent, numberOfGames, gameCount)
}
package battleship.core
import battleship.core.models._
import battleship.utils.io._
import scala.annotation.tailrec
import scala.util.Random
object Main extends App {
val gameConfig = GameConfig(GameConfig.DEFAULT)
if (gameConfig.gridSize > 10) {
GameDisplay.gridTooBig()
System.exit(1)
}
GameDisplay.clear()
GameDisplay.introduction()
val randoms = Seq[Random](new Random(), new Random())
/**
*
* @param gameType
* @param numberOfGames
* @param randoms
* @param shipConfig
* @return
*/
def initGameStates(numberOfGames: Int, randoms: Seq[Random], gameConfig: GameConfig): Set[GameState] = {
GameDisplay.choseYourName(1)
val namePlayer: String = PlayerInputs.choseName()
val player: HumanPlayer = HumanPlayer.createPlayer(namePlayer, randoms(0), gameConfig.shipsConfig, gameConfig.gridSize)
val ia: WeakIAPlayer = WeakIAPlayer.generateIA(1, randoms(1), gameConfig.shipsConfig, gameConfig.gridSize)
Set(GameState(player, ia, numberOfGames, 1))
}
/**
*
* @param gameState
* @param shipsConfig
* @return
*/
@tailrec
def mainLoop(gameState: GameState, gameConfig: GameConfig): (Player, Player) = {
val currentPlayer = gameState.currentPlayer
val opponent = gameState.opponent
val isCurrentPlayerHuman: Boolean = currentPlayer.isInstanceOf[HumanPlayer]
val winner = gameState.isThereAWinner()
if (winner.isEmpty) {
if (isCurrentPlayerHuman) {
GameDisplay.clear()
PlayerDisplay.show(currentPlayer, opponent)
GridDisplay.showPlayerGrid(currentPlayer.ships, opponent.shots.keys.toSeq, gameConfig.gridSize)
GridDisplay.showOpponentGrid(currentPlayer.shots, gameConfig.gridSize)
PlayerDisplay.shoot()
}
val target: (Int, Int) = currentPlayer.shoot(gameConfig.gridSize)
val (newOpponent, touched, shipSunk): (Player, Boolean, Option[Ship]) = opponent.receiveShoot(target)
if (isCurrentPlayerHuman) {
if (shipSunk.isDefined) PlayerDisplay.sunk(shipSunk.get.name) else if (touched) PlayerDisplay.touched() else PlayerDisplay.notTouched()
GameDisplay.endOfTurn()
PlayerInputs.pressAKey()
}
val newCurrentPlayer = currentPlayer.didShoot(target, didTouch = touched)
mainLoop(GameState(newOpponent, newCurrentPlayer, gameState.numberOfGames, gameState.gameCount), gameConfig)
} else {
val addedVictoryWinner = winner.get.addVictory()
val continue: Boolean = if (currentPlayer.isInstanceOf[HumanPlayer] || opponent.isInstanceOf[HumanPlayer]) {
GameDisplay.winner(addedVictoryWinner.name)
GameDisplay.continue()
PlayerInputs.continue() != "q"
} else {
gameState.gameCount < gameState.numberOfGames
}
if (continue) {
GameDisplay.clear()
GameDisplay.gameNumber(gameState.gameCount + 1, gameState.numberOfGames)
mainLoop(GameState(currentPlayer.reset(gameConfig.shipsConfig, gameConfig.gridSize), addedVictoryWinner.reset(gameConfig.shipsConfig, gameConfig.gridSize), gameState.numberOfGames, gameState.gameCount + 1), gameConfig)
} else {
(currentPlayer, addedVictoryWinner)
}
}
}
val gameStates = initGameStates(100, randoms, gameConfig)
val results = gameStates.map(gameState => {
GameDisplay.gameNumber(gameState.gameCount, gameState.numberOfGames)
mainLoop(gameState, gameConfig)
})
GameDisplay.end(results)
}
package battleship.core.models
import battleship.utils.io.PlayerInputs
import battleship.utils.ships.Generator
import scala.collection.immutable.Seq
import scala.util.Random
case class HumanPlayer(ships: Seq[Ship], name: String, shots: Map[(Int, Int), Boolean], receivedShots: Seq[(Int, Int)], numberOfWins: Int, random: Random) extends Player {
/**
* Get the coordinates of the target
*
* @param gridSize Size of the grid to shoot
* @return (Int, Int) A tuple that indicates where the player is shooting
*/
override def shoot(gridSize: Int): (Int, Int) = {
PlayerInputs.getPoint(gridSize)
}
/**
*
* @param shot Coordinate of the shot
* @return (Player, Bookean) A tuple that contains the player that has been modified with the shot and a Boolean indicating if the shot hit one of the shi of the player or not
*/
override def receiveShoot(shot: (Int, Int)): (HumanPlayer, Boolean, Option[Ship]) = {
val shipShot: Option[Ship] = ships.find(ship => ship.squares.contains(shot))
shipShot match {
case Some(ship) => {
val newShip: Ship = ship.hit(shot)
val sunk: Boolean = newShip.isSunk()
(HumanPlayer(ships.map { case oldShip if oldShip == ship => newShip; case x => x }, name, shots, receivedShots :+ shot, numberOfWins, random), true, if (sunk) Some(newShip) else None)
}
case None => (HumanPlayer(ships, name, shots, receivedShots :+ shot, numberOfWins, random), false, None)
}
}
/**
*
* @param target Coordinates of the shot
* @param didTouch Boolean that indicates if the shot did touch an opponent ship or not
* @return The player modified
*/
override def didShoot(target: (Int, Int), didTouch: Boolean): HumanPlayer = {
HumanPlayer(ships, name, shots + (target -> didTouch), receivedShots, numberOfWins, random)
}
/**
*
* @return The player modified with a victory added
*/
override def addVictory(): Player = {
this.copy(numberOfWins = numberOfWins + 1)
}
/**
*
* @param shipsConfig The configuration of the ships
* @param gridSize The size of the grid
* @return The player reseted
*/
override def reset(shipsConfig: Map[String, Int], gridSize: Int): HumanPlayer = {
val newShips: Seq[Ship] = Generator.createShips(shipsConfig, Seq[Ship](), gridSize)
this.copy(ships = newShips, shots = Map[(Int, Int), Boolean](), receivedShots = Seq[(Int, Int)]())
}
}
object HumanPlayer {
/**
*
* @param name The name of the player
* @param random An instance of the random class
* @param shipsConfig The config of the ships
* @param gridSize The size of the grid
* @return A new player configured manually
*/
def createPlayer(name: String, random: Random, shipsConfig: Map[String, Int], gridSize: Int): HumanPlayer = {
val ships = Generator.createShips(shipsConfig, Seq[Ship](), gridSize)
new HumanPlayer(ships, name, shots = Map[(Int, Int), Boolean](), Seq[(Int, Int)](), 0, random)
}
}
\ No newline at end of file
package battleship.core.models
import scala.annotation.tailrec
import scala.collection.immutable.Seq
import scala.util.Random
trait Player {
val name: String
val ships: Seq[Ship]
val shots: Map[(Int, Int), Boolean]
val receivedShots: Seq[(Int, Int)]
val numberOfWins: Int
val random: Random
/**
* Get the coordinates of the target
*
* @param gridSize Size of the grid to shoot
* @return (Int, Int) A tuple that indicates where the player is shooting
*/
def shoot(gridSize: Int): (Int, Int)
/**
* The player receive a shot
*
* @param shot Coordinate of the shot
* @return (Player, Bookean) A tuple that contains the player that has been modified with the shot and a Boolean indicating if the shot hit one of the shi of the player or not
*/
def receiveShoot(shot: (Int, Int)): (Player, Boolean, Option[Ship])
/**
* Method the count the number of ships that are still standing in the player's fleet
*
* @return Int The number of ships still standing
*/
def numberOfShipsLeft(): Int = {
@tailrec
def numberOfShipsLeftTR(ships: Seq[Ship], number: Int): Int = {
val currentShip = ships.headOption
currentShip match {
case None => number
case Some(ship) => if (ship.isSunk()) numberOfShipsLeftTR(ships.tail, number) else numberOfShipsLeftTR(ships.tail, number + 1)
}
}
numberOfShipsLeftTR(ships, 0)
}
/**
* Method that has to be implemented to indicate what to do with the results of the shot
*
* @param target Coordinated of the shot
* @param didTouch Boolean that indicates if the shot did touch an opponent ship or not
* @return The player modified
*/
def didShoot(target: (Int, Int), didTouch: Boolean): Player
/**
* Method that has to be override to indicate what to do when a player win a game
*
* @return The player modified with a victory added
*/
def addVictory(): Player
/**
* Reset the player ships
*
* @param shipsConfig The configuration of the ships
* @param gridSize The size of the grid
* @return The player reseted
*/
def reset(shipsConfig: Map[String, Int], gridSize: Int): Player
}
object Player {
val LEFT = 1
val UP = 2
val RIGHT = 3
val DOWN = 4
/**
*
* Find the next slot to process
*
* @param origin The origin of the shot
* @param slot The actual slot to process
* @param radius The actual radius between the origin and the slot
* @param direction The current direction of the process
* @return A tuple containing three objects: 1st one is the next slot to process, 2nd one is the next direction to process and the last one is if the radius is growing
*/
def nextSlot(origin: (Int, Int), slot: (Int, Int), radius: Int, direction: Int): ((Int, Int), Int, Boolean) = {
slot match {
case _ if origin._1 - radius == slot._1 && origin._2 == slot._2 => {
((slot._1 - 1, slot._2 - 1), UP, true)
}
case _ => {
direction match {
case LEFT => {
if (origin._1 - radius == slot._1 && origin._2 + radius == slot._2)
((slot._1, slot._2 - 1), UP, false)
else
((slot._1 - 1, slot._2), LEFT, false)
}
case UP => {
if (origin._1 - radius == slot._1 && origin._2 - radius == slot._2)
((slot._1 + 1, slot._2), RIGHT, false)
else
((slot._1, slot._2 - 1), UP, false)
}
case RIGHT => {
if (origin._1 + radius == slot._1 && origin._2 - radius == slot._2)
((slot._1, slot._2 + 1), DOWN, false)
else
((slot._1 + 1, slot._2), RIGHT, false)
}
case _ => {
if (origin._1 + radius == slot._1 && origin._2 + radius == slot._2)
((slot._1 - 1, slot._2), LEFT, false)
else
((slot._1, slot._2 + 1), DOWN, false)
}
}
}
}
}
/**
* FindClosestFreeSlot is finding the closest slot that the player did not shot from an origin point.
* It is using a snail algorithm that allows to check the close slots first
*
* @param origin The origin point
* @param shots The shots of the player
* @param radius The current radius
* @param slot The current slot that is processed
* @param direction The current direction of the algorithm
* @param gridSize The size of the grid
* @return The closest slot not shot
*/
@tailrec
def findClosestFreeSlot(origin: (Int, Int), shots: Map[(Int, Int), Boolean], radius: Int, slot: (Int, Int), direction: Int, gridSize: Int): (Int, Int) = {
if (
slot._1 >= gridSize ||
slot._1 < 0 ||
slot._2 >= gridSize ||
slot._2 < 0 ||
shots.contains(slot)
) {
val newSlotAndDirection = nextSlot(origin, slot, radius, direction)
findClosestFreeSlot(origin, shots, if (newSlotAndDirection._3) radius + 1 else radius, newSlotAndDirection._1, newSlotAndDirection._2, gridSize)
} else {
slot
}
}
}
\ No newline at end of file
package battleship.core.models
import scala.annotation.tailrec
case class Ship(name: String, squares: Map[(Int, Int), Boolean]) {
/**
*
* @return True if the ship is sunk false otherwise
*/
def isSunk(): Boolean = {
squares.forall(point => point._2)
}
/**
*
* @param shot The coordinates where the ship is hit
* @return The ship modified with his slot hit updated to true
*/
def hit(shot: (Int, Int)): Ship = {
Ship(name, squares.updated(shot, true))
}
}
object Ship {
val HORIZONTAL = "H"
val VERTICAL = "V"
val CARRIER = "Carrier"
val BATTLESHIP = "Battleship"
val CRUISER = "Cruiser"
val SUBMARINE = "Submarine"
val DESTROYER = "Destroyer"
/**
* Use a tail recursion within the method
*
* @param name Name of the ship
* @param direction Direction of the ship (Ship.HORIZONTAL or Ship.VERTICAl)
* @param point The origin of the ship
* @param shipsConfig The configuration of the ships
* @return A ship configured
*/
def convertInputsToShip(name: String, direction: String, point: (Int, Int), shipsConfig: Map[String, Int]): Ship = {
@tailrec
def convertInputsToShipTR(name: String, squareLeft: Int, direction: String, point: (Int, Int), squares: Map[(Int, Int), Boolean], shipsConfig: Map[String, Int]): Ship = {
squareLeft match {
case 0 => Ship(name, squares)
case _ => {
val newSquares = squares.+(point -> false)
val newPoint = if (direction == Ship.HORIZONTAL) (point._1, point._2 + 1) else (point._1 + 1, point._2)
convertInputsToShipTR(name, squareLeft - 1, direction, newPoint, newSquares, shipsConfig)
}
}
}
convertInputsToShipTR(name, shipsConfig(name), direction, point, Map[(Int, Int), Boolean](), shipsConfig)
}
}
package battleship.core.models
/**
* Class that describes the direction and origin of a ship
*
* @param direction Direction Ship.HORIZONTAL or SHIP.VERTICAL
* @param point Origin of the ship
*/
case class ShipInformation(direction: String, point: (Int, Int))
package battleship.core.models
import battleship.utils.ships.Generator
import scala.collection.immutable.Seq
import scala.util.Random
case class WeakIAPlayer(ships: Seq[Ship], name: String, shots: Map[(Int, Int), Boolean], receivedShots: Seq[(Int, Int)], random: Random, numberOfWins: Int) extends Player {
/**
*
* @param gridSize Size of the grid to shoot
* @return (Int, Int) A tuple that indicates where the player is shooting
*/
override def shoot(gridSize: Int): (Int, Int) = {
(random.nextInt(gridSize), random.nextInt(gridSize))
}
/**
*
* @param shot Coordinate of the shot
* @return (Player, Bookean) A tuple that contains the player that has been modified with the shot and a Boolean indicating if the shot hit one of the shi of the player or not
*/
override def receiveShoot(shot: (Int, Int)): (WeakIAPlayer, Boolean, Option[Ship]) = {
val shipShot: Option[Ship] = ships.find(ship => ship.squares.contains(shot))
shipShot match {
case Some(ship) => {
val newShip: Ship = ship.hit(shot)
val sunk: Boolean = newShip.isSunk()
(WeakIAPlayer(ships.map { case oldShip if oldShip == ship => newShip; case x => x }, name, shots, receivedShots :+ shot, random, numberOfWins), true, if (sunk) Some(newShip) else None)
}
case None => (WeakIAPlayer(ships, name, shots, receivedShots :+ shot, random, numberOfWins), false, None)
}
}
/**
*
* @param target Coordinated of the shot
* @param didTouch Boolean that indicates if the shot did touch an opponent ship or not
* @return The player modified
*/
override def didShoot(target: (Int, Int), didTouch: Boolean): WeakIAPlayer = {
WeakIAPlayer(ships, name, shots + (target -> didTouch), receivedShots, random, numberOfWins)
}
/**
*
* @return The player modified with a victory added
*/
override def addVictory(): WeakIAPlayer = {
this.copy(numberOfWins = numberOfWins + 1)
}
/**
*
* @param shipsConfig The configuration of the ships
* @param gridSize The size of the grid
* @return A reseted Strong AI player
*/
override def reset(shipsConfig: Map[String, Int], gridSize: Int): Player = {
val newShips: Seq[Ship] = Generator.randomShips(shipsConfig, Seq[Ship](), this.random, gridSize)
this.copy(ships = newShips, shots = Map[(Int, Int), Boolean](), receivedShots = Seq[(Int, Int)]())
}
}
object WeakIAPlayer {
/**
*
* @param index The index of the IA
* @param random Instance of the random class
* @param shipsConfig The configuration of the ships
* @param gridSize The size of the grid
* @return A player configured randomly
*/
def generateIA(index: Int, random: Random, shipsConfig: Map[String, Int], gridSize: Int): WeakIAPlayer = {
val ships: Seq[Ship] = Generator.randomShips(shipsConfig, Seq[Ship](), random, gridSize)
WeakIAPlayer(ships, "Weak IA " + index, Map[(Int, Int), Boolean](), Seq[(Int, Int)](), random, 0)
}
}
package battleship.utils.io
import battleship.core.models.Player
object GameDisplay {
def winner(name: String) = {
println(name + " won this game, good job.")
println()
}
def continue() = {
println("Press a key to play a new game (q to quit)")
}
def introduction(): Unit = {
print(
"""
█████████████████████████████████████████████████████████
█▄─▄─▀██▀▄─██─▄─▄─█─▄─▄─█▄─▄███▄─▄▄─█─▄▄▄▄█─█─█▄─▄█▄─▄▄─█
██─▄─▀██─▀─████─█████─████─██▀██─▄█▀█▄▄▄▄─█─▄─██─███─▄▄▄█
▀▄▄▄▄▀▀▄▄▀▄▄▀▀▄▄▄▀▀▀▄▄▄▀▀▄▄▄▄▄▀▄▄▄▄▄▀▄▄▄▄▄▀▄▀▄▀▄▄▄▀▄▄▄▀▀▀
""".stripMargin)
}
def end(results: Set[(Player, Player)]): Unit = {
println()
results.zipWithIndex.foreach(resultZip => {
val game = resultZip._1
val index = resultZip._2
println("Fight " + (index + 1) + s": ${game._1.name} VS ${game._2.name}".toUpperCase())
println()
println(s"${game._1.name} won ${game._1.numberOfWins} time${if (game._1.numberOfWins > 1) 's' else ""} ")
println(s"${game._2.name} won ${game._2.numberOfWins} time${if (game._2.numberOfWins > 1) 's' else ""} ")
println()
println()
})
println("You can see the results in the results.csv file.\n")
}
def endOfTurn() = {
println(s"End of your turn, press enter to continue...")
}
def gameNumber(number: Int, oufOf: Int): Unit = {
println("Game " + number + "/" + oufOf)
}
def opponentsTurn(name: String) = {
println(name + " it is your turn -> Press enter to continue...")
}
def gridTooBig() = {
println("The grid size is too big, change the game config and rebuild, exit...")
}
def choseYourName(playerNumber: Int) = {
println(s"Chose your name")
}
def gameIsOver(player: Player): Unit = {
println(
s"""
|${player.name} won the game !!!
""".stripMargin)
}
def Introduction(): Unit = {
}
def clear(): Unit = {
print("\033[H\033[2J")
}
}
package battleship.utils.io
import battleship.core.models.Ship
import scala.annotation.tailrec
object GridDisplay {
private val TOUCHED: String = Console.RED
private val NOT_TOUCHED: String = Console.BLACK
private val WATER: String = Console.BLUE
private val MISSED: String = Console.YELLOW
private val BLOCK: String = "███"
private val BASE_COLOR: String = Console.WHITE
private def showGrid(grid: List[List[String]], gridSize: Int): Unit = {
println()
@tailrec
def printLineNumbers(actual: Int, n: Int): Unit = {
actual match {
case _ if actual < n => print(s" ${actual} "); printLineNumbers(actual + 1, n)
case _ => {
println()
}
}
}
printLineNumbers(0, gridSize)
@tailrec
def showGridTR(grid: List[List[String]], x: Int, y: Int, gridSize: Int): Unit = {
if (x != gridSize) {
if (y != gridSize) {
print(grid(x)(y))
showGridTR(grid, x, y + 1, gridSize)
} else {
print(s"${BASE_COLOR} ${(x + 'A').toChar}");
println()
showGridTR(grid, x + 1, 0, gridSize)
}
} else {
print(BASE_COLOR)
}
}
showGridTR(grid, 0, 0, gridSize)
println()
}
def showPlayerGrid(ships: Seq[Ship], shots: Seq[(Int, Int)], gridSize: Int): Unit = {
println(s"Here are your ships ->")
var grid = List.fill[List[String]](gridSize)(List.fill(gridSize)(WATER + BLOCK))
shots.foreach((shot) => {
grid = grid.updated(shot._1, grid(shot._1).updated(shot._2, MISSED + BLOCK))
})
ships.foreach((ship) => {
ship.squares.foreach(point => {
val touched: Boolean = point._2
if (touched) {
grid = grid.updated(point._1._1, grid(point._1._1).updated(point._1._2, TOUCHED + BLOCK))
} else {
grid = grid.updated(point._1._1, grid(point._1._1).updated(point._1._2, NOT_TOUCHED + BLOCK))
}
})
})
showGrid(grid, gridSize)
}
def showOpponentGrid(shots: Map[(Int, Int), Boolean], gridSize: Int): Unit = {
println(s"Here are your shots->")
var grid = List.fill[List[String]](gridSize)(List.fill(gridSize)(WATER + BLOCK))
shots.foreach((shot) => {
grid = grid.updated(shot._1._1, grid(shot._1._1).updated(shot._1._2, if (shot._2) TOUCHED + BLOCK else NOT_TOUCHED + BLOCK))
})
showGrid(grid, gridSize)
}
}
package battleship.utils.io
import battleship.core.models.{Player, Ship}
object PlayerDisplay {
def sunk(name: String): Unit = {
println(name.toUpperCase() + " SUNK !!! Well done !")
}
def touched(): Unit = {
println("HIT !")
}
def notTouched(): Unit = {
println("Not hit, too bad...")
}
def shoot(): Unit = {
println("Choose your target (example: A3) ->")
}
def problemPlacingShip(ship: Ship): Unit = {
println(s"Problem placing the ${ship.name}")
}
def placeYourShips(namePlayer: String): Unit = {
println(s"${namePlayer}")
}
def getOriginShip(nameShip: String, sizeShip: Int): Unit = {
println("What is the origin of your ship (example: A3) ->")
}
def setNewShip(nameShip: String, sizeShip: Int): Unit = {
println("You have to place the " + nameShip + " -> which is composed of " + sizeShip + " units:\n" +
"Will it be horizontal (" + Ship.HORIZONTAL + ") or vertical (" + Ship.VERTICAL + ") ?")
}
def show(player: Player, opponent: Player): Unit = {
println(s"${player.name} it is your turn ->")
}
}
package battleship.utils.io
import battleship.core.models.{Ship, ShipInformation}
import scala.annotation.tailrec
import scala.io.StdIn
object PlayerInputs {
def continue() = {
StdIn.readLine()
}
def pressAKey() = {
StdIn.readLine()
}
@tailrec
def choseName(): String = {
val name = StdIn.readLine()
val Pattern = """(^\w+$)""".r
name match {
case Pattern(c) => c
case _ => choseName()
}
}
def getShipInformation(shipConfig: (String, Int), gridSize: Int): ShipInformation = {
PlayerDisplay.setNewShip(shipConfig._1, shipConfig._2)
val direction: String = PlayerInputs.getDirection()
PlayerDisplay.getOriginShip(shipConfig._1, shipConfig._2)
val point: (Int, Int) = PlayerInputs.getPoint(gridSize)
ShipInformation(direction, point)
}
@tailrec
def getDirection(): String = {
val direction = StdIn.readLine().toUpperCase
direction match {
case Ship.HORIZONTAL | Ship.VERTICAL => direction
case _ => getDirection()
}
}
def choiceOfPlayers(): Int = {
val choice = StdIn.readLine()
val Pattern = "(^[1-5]$)".r
choice match {
case Pattern(p) => {
p.toInt
}
case _ => choiceOfPlayers()
}
}
@tailrec
def getPoint(gridSize: Int): (Int, Int) = {
val coordinates: String = StdIn.readLine()
val Pattern = ("(^[a-zA-Z][0-" + (gridSize - 1) + "]$)").r
coordinates match {
case Pattern(p) => {
val line = p(0).toUpper.toInt - 'A'
if (line - gridSize < 0)
(line, p(1) - '0'.toInt)
else
getPoint(gridSize)
}
case _ => getPoint(gridSize)
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment