Skip to content
Snippets Groups Projects
Commit b431bbb5 authored by Dumindu Weligamage's avatar Dumindu Weligamage
Browse files

Merge branch 'dev' into 'main'

Version 1.0

See merge request !1
parents 09e5c042 e9da11b8
Branches main
No related tags found
1 merge request!1Version 1.0
Showing
with 1988 additions and 90 deletions
*.class
*.log
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
.idea
.metals
.vscode
# Battleship-Game
# Battleship-Scala
## The game has been written in a functional programming way
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
```
cd existing_repo
git remote add origin https://gitlab.utu.fi/dmweli/battleship-game.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](https://gitlab.utu.fi/dmweli/battleship-game/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
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