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