diff --git a/.github/workflows/android_build.yml b/.github/workflows/android_build.yml
index 0efb61b..c7966c4 100644
--- a/.github/workflows/android_build.yml
+++ b/.github/workflows/android_build.yml
@@ -18,6 +18,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
+ - name: Set up Java
+ uses: actions/setup-java@v2
+ with:
+ java-version: 17
+ distribution: "temurin"
+
- name: Restore Cache
uses: actions/cache@v2
with:
@@ -46,6 +52,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
+ - name: Set up Java
+ uses: actions/setup-java@v2
+ with:
+ java-version: 17
+ distribution: "temurin"
+
- name: Restore Cache
uses: actions/cache@v2
with:
@@ -74,6 +86,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
+ - name: Set up Java
+ uses: actions/setup-java@v2
+ with:
+ java-version: 17
+ distribution: "temurin"
+
- name: Restore Cache
uses: actions/cache@v2
with:
@@ -90,4 +108,4 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: apk
- path: app/build/outputs/apk/debug/**.apk
\ No newline at end of file
+ path: app/build/outputs/apk/debug/**.apk
diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index f0182ec..7138d94 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -10,12 +10,12 @@
-
+
-
+
diff --git a/app/build.gradle b/app/build.gradle
index b6f58cb..4f4ce21 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,8 +15,8 @@ android {
applicationId "com.avs.sea.battle"
minSdkVersion 19
targetSdkVersion 34
- versionCode 9
- versionName "9"
+ versionCode 11
+ versionName "1.0.$versionCode"
vectorDrawables.useSupportLibrary = true
resourceConfigurations += ['en', 'uk', 'ru']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/app/release/app-release.aab b/app/release/app-release.aab
new file mode 100644
index 0000000..2cb6c51
Binary files /dev/null and b/app/release/app-release.aab differ
diff --git a/app/src/main/java/com/avs/sea/battle/battle_field/BattleField.kt b/app/src/main/java/com/avs/sea/battle/battle_field/BattleField.kt
index 4820814..70ed022 100644
--- a/app/src/main/java/com/avs/sea/battle/battle_field/BattleField.kt
+++ b/app/src/main/java/com/avs/sea/battle/battle_field/BattleField.kt
@@ -48,27 +48,101 @@ class BattleField : BaseBattleField() {
printBattleField()
}
- fun handleShot(coordinate: Coordinate?): Boolean {
+ fun handleShot(coordinate: Coordinate?): Pair> {
var isShipHit = false
+ var killedShipCoordinates: ArrayList = ArrayList()
if (coordinate != null && coordinate.x in 0 until SQUARES_COUNT && coordinate.y in 0 until SQUARES_COUNT) {
if (battleField[coordinate.x][coordinate.y]?.getCellState() == CellState.EMPTY) {
battleField[coordinate.x][coordinate.y]?.setCellState(CellState.SHOT_FAILURE)
} else {
battleField[coordinate.x][coordinate.y]?.setCellState(CellState.SHOT_SUCCESS)
- defineShipByCoordinate(coordinate)
isShipHit = true
+ val ship = getShipByCoordinate(coordinate)
+ ship?.let {
+ it.setShotSuccessState(coordinate)
+ if (it.isDead()) {
+ markNeighbours(ship)
+ killedShipCoordinates = getShipCoordinates(ship)
+ }
+ }
+ }
+ }
+ return isShipHit to killedShipCoordinates
+ }
+
+ private fun markNeighbours(ship: Ship) {
+ if (ship.getShipOrientation() == Orientation.VERTICAL) {
+ markVerticalNeighbours(ship)
+ } else {
+ markHorizontalNeighbours(ship)
+ }
+ }
+
+ private fun markHorizontalNeighbours(ship: Ship) {
+ for (cell in ship.getShipCells()) {
+ if (cell.getX() != 0) battleField[cell.getX() - 1][cell.getY()]?.setCellState(CellState.SHOT_FAILURE)
+ if (cell.getX() != battleField.size - 1) battleField[cell.getX() + 1][cell.getY()]?.setCellState(
+ CellState.SHOT_FAILURE
+ )
+ }
+ val fistCell = ship.getShipCells().first()
+ if (fistCell.getY() != 0) {
+ battleField[fistCell.getX()][fistCell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ if (fistCell.getX() != 0) {
+ battleField[fistCell.getX() - 1][fistCell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ if (fistCell.getX() != battleField.size - 1) {
+ battleField[fistCell.getX() + 1][fistCell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ }
+ val lastCell = ship.getShipCells().last()
+ if (lastCell.getY() != battleField.size - 1) {
+ battleField[lastCell.getX()][lastCell.getY() + 1]?.setCellState(CellState.SHOT_FAILURE)
+ if (lastCell.getX() != 0) {
+ battleField[lastCell.getX() - 1][lastCell.getY() + 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ if (lastCell.getX() != battleField.size - 1) {
+ battleField[lastCell.getX() + 1][lastCell.getY() + 1]?.setCellState(CellState.SHOT_FAILURE)
}
}
- return isShipHit
}
- private fun defineShipByCoordinate(coordinate: Coordinate) {
+ private fun markVerticalNeighbours(ship: Ship) {
+ for (cell in ship.getShipCells()) {
+ if (cell.getY() != 0) battleField[cell.getX()][cell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ if (cell.getY() != battleField.size - 1) battleField[cell.getX()][cell.getY() + 1]?.setCellState(
+ CellState.SHOT_FAILURE
+ )
+ }
+ val fistCell = ship.getShipCells().first()
+ if (fistCell.getX() != 0) {
+ battleField[fistCell.getX() - 1][fistCell.getY()]?.setCellState(CellState.SHOT_FAILURE)
+ if (fistCell.getY() != 0) {
+ battleField[fistCell.getX() - 1][fistCell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ if (fistCell.getY() != battleField.size - 1) {
+ battleField[fistCell.getX() - 1][fistCell.getY() + 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ }
+ val lastCell = ship.getShipCells().last()
+ if (lastCell.getX() != battleField.size - 1) {
+ battleField[lastCell.getX() + 1][lastCell.getY()]?.setCellState(CellState.SHOT_FAILURE)
+ if (lastCell.getY() != 0) {
+ battleField[lastCell.getX() + 1][lastCell.getY() - 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ if (lastCell.getY() != battleField.size - 1) {
+ battleField[lastCell.getX() + 1][lastCell.getY() + 1]?.setCellState(CellState.SHOT_FAILURE)
+ }
+ }
+ }
+
+ private fun getShipByCoordinate(coordinate: Coordinate): Ship? {
for (ship in ships) {
if (coordinate.x in ship.getRowCoordinates() && coordinate.y in ship.getColumnCoordinates()) {
- ship.setShotSuccessState(coordinate)
- break
+ return ship
}
}
+ return null
}
fun isGameOver(): Boolean {
@@ -94,12 +168,10 @@ class BattleField : BaseBattleField() {
return shipsCoordinates
}
- fun getAllShipsCoordinates(): ArrayList {
+ private fun getShipCoordinates(ship: Ship): ArrayList {
val shipsCoordinates = arrayListOf()
- ships.forEach { ship ->
- ship.getShipCells().forEach { cell ->
- shipsCoordinates.add(cell.getCoordinate())
- }
+ ship.getShipCells().forEach { cell ->
+ shipsCoordinates.add(cell.getCoordinate())
}
return shipsCoordinates
}
diff --git a/app/src/main/java/com/avs/sea/battle/main/MainActivity.kt b/app/src/main/java/com/avs/sea/battle/main/MainActivity.kt
index adf4e3e..9c84079 100644
--- a/app/src/main/java/com/avs/sea/battle/main/MainActivity.kt
+++ b/app/src/main/java/com/avs/sea/battle/main/MainActivity.kt
@@ -9,7 +9,6 @@ import android.util.Log
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
-import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
@@ -118,12 +117,6 @@ class MainActivity : AppCompatActivity(), PopupMenu.OnMenuItemClickListener {
}
}
- private fun setTextColor(v: View, color: Int) {
- if (v is TextView) v.setTextColor(
- ContextCompat.getColor(this, color)
- )
- }
-
private fun showPopup(v: View?) {
val wrapper: Context = ContextThemeWrapper(this, R.style.PopupStyle)
if (v != null) {
diff --git a/app/src/main/java/com/avs/sea/battle/main/MainViewModel.kt b/app/src/main/java/com/avs/sea/battle/main/MainViewModel.kt
index e398ca3..9e7f5de 100644
--- a/app/src/main/java/com/avs/sea/battle/main/MainViewModel.kt
+++ b/app/src/main/java/com/avs/sea/battle/main/MainViewModel.kt
@@ -105,9 +105,12 @@ class MainViewModel : ViewModel() {
fun makeFireAsPerson() {
if (activePlayer == Player.PERSON) {
- val isShipHit = computerBattleField.handleShot(_selectedByPersonCoordinate.value)
+ val shipState = computerBattleField.handleShot(_selectedByPersonCoordinate.value)
_selectedByPersonCoordinate.value = null
- if (isShipHit) {
+ if (shipState.first) {
+ if (shipState.second.isNotEmpty()) {
+ _personFailShots.value = computerBattleField.getDotsCoordinates()
+ }
_status.value = R.string.status_shot_ship_again_text
_personSuccessfulShots.value = computerBattleField.getCrossesCoordinates()
if (computerBattleField.isGameOver()) {
@@ -127,12 +130,15 @@ class MainViewModel : ViewModel() {
private fun playAsComputer() {
val coordinate: Coordinate = shotManager.getCoordinateToShot()
_selectedByComputerCoordinate.value = coordinate
- val isShipHit = personBattleField.handleShot(coordinate)
- shotManager.handleShot(isShipHit)
- if (isShipHit) {
+ val shipState = personBattleField.handleShot(coordinate)
+ shotManager.handleShot(shipState)
+ if (shipState.first) {
viewModelScope.launch {
delay(SECOND_IN_MILLIS)
_computerSuccessfulShots.value = personBattleField.getCrossesCoordinates()
+ if (shipState.second.isNotEmpty()) {
+ _computerFailShots.value = personBattleField.getDotsCoordinates()
+ }
if (personBattleField.isGameOver()) {
endGame(false)
} else {
@@ -161,7 +167,7 @@ class MainViewModel : ViewModel() {
private fun endGame(isPersonWon: Boolean) {
activePlayer = Player.NONE
- _computerShips.value = computerBattleField.getAllShipsCoordinates()
+ _computerShips.value = computerBattleField.getShipsCoordinates()
if (isPersonWon) {
_endGameEvent.value = true to Player.PERSON
_status.value = R.string.status_game_over_you_win_text
diff --git a/app/src/main/java/com/avs/sea/battle/main/ShotManager.kt b/app/src/main/java/com/avs/sea/battle/main/ShotManager.kt
index 0a30868..af1cbb4 100644
--- a/app/src/main/java/com/avs/sea/battle/main/ShotManager.kt
+++ b/app/src/main/java/com/avs/sea/battle/main/ShotManager.kt
@@ -22,29 +22,17 @@ class ShotManager {
ONE_DECK_SHIP_SIZE, ONE_DECK_SHIP_SIZE, ONE_DECK_SHIP_SIZE, ONE_DECK_SHIP_SIZE
)
- fun getShipsSize(): Int {
- return ships.size
- }
+ fun getShipsSize(): Int = ships.size
- fun getBattleField(): BaseBattleField {
- return battleField
- }
+ fun getBattleField(): BaseBattleField = battleField
- fun getFirstCell(): Cell {
- return firstCell
- }
+ fun getFirstCell(): Cell = firstCell
- fun getSecondCell(): Cell {
- return secondCell
- }
+ fun getSecondCell(): Cell = secondCell
- fun getThirdCell(): Cell {
- return thirdCell
- }
+ fun getThirdCell(): Cell = thirdCell
- fun getFourthCell(): Cell {
- return fourthCell
- }
+ fun getFourthCell(): Cell = fourthCell
fun getCoordinateToShot(): Coordinate {
var coordinate = Coordinate()
@@ -65,12 +53,7 @@ class ShotManager {
) {
coordinate = shotFourthCell(coordinate)
} else {
- coordinate = resetValuesAfterShipIsDead(
- FOUR_DECK_SHIP_SIZE, mutableListOf(
- firstCell.getCoordinate(), secondCell.getCoordinate(),
- thirdCell.getCoordinate(), fourthCell.getCoordinate()
- )
- )
+ coordinate = getRandomCoordinate()
}
return coordinate
}
@@ -81,11 +64,7 @@ class ShotManager {
coordinateFourth = checkNeighbourCells(firstCell, thirdCell)
}
if (coordinateFourth.x == -1) {
- coordinateFourth = resetValuesAfterShipIsDead(
- THREE_DECK_SHIP_SIZE, mutableListOf(
- firstCell.getCoordinate(), secondCell.getCoordinate(), thirdCell.getCoordinate()
- )
- )
+ coordinateFourth = getRandomCoordinate()
} else {
fourthCell = Cell(coordinateFourth.x, coordinateFourth.y)
}
@@ -99,10 +78,7 @@ class ShotManager {
coordinateThird = checkNeighbourCells(firstCell, secondCell)
}
if (coordinateThird.x == -1) {
- coordinateThird = resetValuesAfterShipIsDead(
- TWO_DECK_SHIP_SIZE,
- mutableListOf(firstCell.getCoordinate(), secondCell.getCoordinate())
- )
+ coordinateThird = getRandomCoordinate()
} else {
thirdCell = Cell(coordinateThird.x, coordinateThird.y)
}
@@ -118,24 +94,17 @@ class ShotManager {
coordinateSecond = getNextCoordinateToShot(firstCell)
}
if (coordinateSecond.x == -1) {
- coordinateSecond = resetValuesAfterShipIsDead(
- ONE_DECK_SHIP_SIZE,
- mutableListOf(firstCell.getCoordinate())
- )
+ coordinateSecond = getRandomCoordinate()
} else {
secondCell = Cell(coordinateSecond.x, coordinateSecond.y)
}
return coordinateSecond
}
- fun resetValuesAfterShipIsDead(
- deadShip: Int,
- cells: MutableList
- ): Coordinate {
- ships.remove(deadShip)
- markEdgeCells(cells)
+ fun resetValuesAfterShipIsDead(deadShipCoordinates: MutableList) {
+ ships.remove(deadShipCoordinates.size)
+ markEdgeCells(deadShipCoordinates)
resetCells()
- return getRandomCoordinate()
}
fun markEdgeCells(cells: MutableList) {
@@ -162,17 +131,17 @@ class ShotManager {
fun getMaxCoordinate(list: MutableList, orientation: Orientation): Coordinate {
return if (orientation == Orientation.VERTICAL) {
- list.maxByOrNull { it.x }!!
+ list.maxBy { it.x }
} else {
- list.maxByOrNull { it.y }!!
+ list.maxBy { it.y }
}
}
fun getMinCoordinate(list: MutableList, orientation: Orientation): Coordinate {
return if (orientation == Orientation.VERTICAL) {
- list.minByOrNull { it.x }!!
+ list.minBy { it.x }
} else {
- list.minByOrNull { it.y }!!
+ list.minBy { it.y }
}
}
@@ -267,7 +236,8 @@ class ShotManager {
return firstCell.getCoordinate()
}
- fun handleShot(shipHit: Boolean) {
+ fun handleShot(shipState: Pair>) {
+ val shipHit = shipState.first
if (firstCell.isState(EMPTY) || firstCell.isState(SHOT_FAILURE)) {
updateBattleField(shipHit, firstCell)
} else if (firstCell.isState(SHOT_SUCCESS)
@@ -294,6 +264,9 @@ class ShotManager {
markNeighbours(fourthCell)
}
}
+ if (shipState.second.isNotEmpty()) {
+ resetValuesAfterShipIsDead(shipState.second)
+ }
}
private fun markNeighbours(cell: Cell) {
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index a51be69..e7e105a 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -5,4 +5,5 @@
#000000
#99C1C0C0
#99808080
+ #662196F3
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 9658c97..3ca9b2c 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -8,5 +8,6 @@
14sp
15sp
16sp
+ 18sp
4.0
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5dba393..27e067b 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -32,7 +32,7 @@
- wrap_content
- @drawable/square_background
- @font/neucha
- - @dimen/text_size_16sp
+ - @dimen/text_size_18sp
- @dimen/dimen_8dp
- 2dp
- @dimen/dimen_8dp
diff --git a/app/src/test/java/com/avs/sea/battle/main/ShotManagerTest.kt b/app/src/test/java/com/avs/sea/battle/main/ShotManagerTest.kt
index addaea2..b8230a1 100644
--- a/app/src/test/java/com/avs/sea/battle/main/ShotManagerTest.kt
+++ b/app/src/test/java/com/avs/sea/battle/main/ShotManagerTest.kt
@@ -234,9 +234,7 @@ class ShotManagerTest {
@Test
fun resetValuesAfterShipIsDead() {
assertEquals(shotManager.getShipsSize(), 10)
- shotManager.resetValuesAfterShipIsDead(
- TWO_DECK_SHIP_SIZE, mutableListOf(Coordinate(1, 1), Coordinate(1, 2))
- )
+ shotManager.resetValuesAfterShipIsDead(mutableListOf(Coordinate(1, 1), Coordinate(1, 2)))
assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(Coordinate(1, 0)))
assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(Coordinate(1, 3)))
resetValues()
@@ -245,37 +243,37 @@ class ShotManagerTest {
@Test
fun getCoordinateToShot() {
- var coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getFirstCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getSecondCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getThirdCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getFourthCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getFirstCell().getCoordinate())
- shotManager.handleShot(false)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getFirstCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getSecondCell().getCoordinate())
- shotManager.handleShot(false)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
- coordinate = shotManager.getCoordinateToShot()
- assertEquals(coordinate, shotManager.getSecondCell().getCoordinate())
- shotManager.handleShot(true)
- assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate))
+ val coordinate1 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate1, shotManager.getFirstCell().getCoordinate())
+ shotManager.handleShot(true to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate1))
+ val coordinate2 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate2, shotManager.getSecondCell().getCoordinate())
+ shotManager.handleShot(true to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate2))
+ val coordinate3 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate3, shotManager.getThirdCell().getCoordinate())
+ shotManager.handleShot(true to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate3))
+ val coordinate4 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate4, shotManager.getFourthCell().getCoordinate())
+ shotManager.handleShot(true to arrayListOf(coordinate1, coordinate2, coordinate3, coordinate4))
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate4))
+ val coordinate5 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate5, shotManager.getFirstCell().getCoordinate())
+ shotManager.handleShot(false to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate5))
+ val coordinate6 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate6, shotManager.getFirstCell().getCoordinate())
+ shotManager.handleShot(true to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate6))
+ val coordinate7 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate7, shotManager.getSecondCell().getCoordinate())
+ shotManager.handleShot(false to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate7))
+ val coordinate8 = shotManager.getCoordinateToShot()
+ assertEquals(coordinate8, shotManager.getSecondCell().getCoordinate())
+ shotManager.handleShot(true to ArrayList())
+ assertFalse(shotManager.getBattleField().isCellFreeToBeSelected(coordinate8))
}
}
\ No newline at end of file