I tried to code the game Battleship(I'm a total beginner) and while switching between activities so the computer will take its turn after the user, the computer does end up taking its turn, but I don't see his screen for some reason, it stays on the user's screen(which is the computer's board).
Anyone can tell me why that is? The board is composed of a 8x8 grid of buttons. After clicking the play button on MainActivity, the program will ask the user to place his battleships, after he's done and clicks the start button, the activity will switch to ComputerSide. The computer will place its battleships randomly on the board and then the user will be asked to pick a target. After the target has been picked and the tile changed color appropriately, the activity will switch to UserSide where the computer will take its turn, and that's when I can't see the activity's screen.
Also as a side note, it keeps saying in the console that frames are being skipped, and I read it's because the main thread is doing too much work, but I don't understand where too much work is being done...
If there's additional information you need to answer let me know please.
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var playButton: Button
private lateinit var rulesButton: Button
private lateinit var aboutButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playButton = findViewById(R.id.btnPlay)
rulesButton = findViewById(R.id.btnRules)
aboutButton = findViewById(R.id.btnAbout)
}
fun playBtnClick(view: View) {
// GameActivity::class.java
val intent = Intent(this, UserSide::class.java)
this.startActivity(intent)
}
fun aboutBtnClick(view: View) {
val intent = Intent(this, AboutActivity::class.java)
this.startActivity(intent)
}
fun rulesBtnClick(view: View) {
val intent = Intent(this, RulesActivity::class.java)
this.startActivity(intent)
}
}
UserSide:
class UserSide : AppCompatActivity() {
/***************** Messages for the user ******************/
val occupiedMsg = { Toast.makeText(this, "Battleships overlapping, choose a different spot", Toast.LENGTH_SHORT).show() }
val noSpaceUpwardsMsg = { Toast.makeText(this, "Not enough space upwards, try again.", Toast.LENGTH_SHORT).show() }
val noSpaceDownwardsMsg = { Toast.makeText(this, "Not enough space downwards, try again.", Toast.LENGTH_SHORT).show() }
val noSpaceLeftMsg = { Toast.makeText(this, "Not enough space to the left, try again.", Toast.LENGTH_SHORT).show() }
val noSpaceRightMsg = { Toast.makeText(this, "Not enough space to the right, try again.", Toast.LENGTH_SHORT).show() }
val notReadyMsg = { Toast.makeText(this, "Not all battleships have been placed.", Toast.LENGTH_SHORT).show() }
val inSessionMsg = { Toast.makeText(this, "Game is in session.", Toast.LENGTH_SHORT).show() }
/***********************************************************************************************************************/
private val UPWARDS = 0
private val DOWNWARDS = 1
private val RIGHT = 2
private val LEFT = 3
private lateinit var instruction: TextView
private lateinit var grid: GridLayout
private lateinit var start: Button
private var tiles: ArrayList<Button> = arrayListOf()
private var battleships: ArrayList<Battleship> = arrayListOf()
private var userInput = -1
private var numOfTiles = 5 // Number of tiles of the current battleship to be placed.
private var placedShips = 0
private var inSession = false
/************************** CHECK FUNCTION verticalCheck **********************/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_side)
start = findViewById(R.id.btnStart)
// Retrieving the instruction at the top.
instruction = findViewById(R.id.instruction)
// Retrieving the game board
grid = findViewById(R.id.gridLayout)
for(btn in grid) {
tiles.add(findViewById(btn.id))
}
// Initializing battleships array
for(num in 0 until 4) {
battleships.add(Battleship(numOfTiles - num))
}
// Adding a listener to all the tiles
for(btn in 1 until tiles.size + 1) {
tiles[btn - 1].setOnClickListener {
placeBattleship(btn)
Thread.sleep(100)
}
}
start.setOnClickListener { startGame() }
}
private fun placeBattleship(btnNum: Int) {
when(numOfTiles) {
// Placing 5 tile battleship
5 -> vertOrHor(btnNum)
// Placing 4 tile battleship
4 -> vertOrHor(btnNum)
// Placing 3 tile battleship
3 -> vertOrHor(btnNum)
// Placing 2 tile battleship
2 -> vertOrHor(btnNum)
}
}
private fun vertOrHor(btnNum: Int) {
// Creating a dialog message and listeners for its buttons.
val dialogClickListener = DialogInterface.OnClickListener { dialog, which ->
when (which) {
// Upwards
DialogInterface.BUTTON_POSITIVE -> {
try {
upOrDown(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceUpwardsMsg() }
}
// Downwards
DialogInterface.BUTTON_NEGATIVE -> {
try {
leftOrRight(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceDownwardsMsg() }
}
}
}
// Editing the text of the dialog and its buttons
val builder: AlertDialog.Builder? = AlertDialog.Builder(this)
builder?.setMessage("Place the battleship vertically or horizontally?")
?.setPositiveButton("Vertically", dialogClickListener)
?.setNegativeButton("Horizontally", dialogClickListener)?.show()
}
private fun upOrDown(btnNum: Int) {
// Creating a dialog message and listeners for its buttons.
val dialogClickListener = DialogInterface.OnClickListener { dialog, which ->
when (which) {
// Upwards
DialogInterface.BUTTON_POSITIVE -> {
try {
userInput = UPWARDS
placeBattleshipVertical(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceUpwardsMsg() }
}
// Downwards
DialogInterface.BUTTON_NEGATIVE -> {
try {
userInput = DOWNWARDS
placeBattleshipVertical(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceDownwardsMsg() }
}
}
}
// Editing the text of the dialog and its buttons
val builder: AlertDialog.Builder? = AlertDialog.Builder(this)
builder?.setMessage("Place the battleship upwards or downwards?")
?.setPositiveButton("Upwards", dialogClickListener)
?.setNegativeButton("Downwards", dialogClickListener)?.show()
}
private fun leftOrRight(btnNum: Int) {
val dialogClickListener = DialogInterface.OnClickListener { dialog, which ->
when (which) {
// Upwards
DialogInterface.BUTTON_POSITIVE -> {
try {
userInput = RIGHT
placeBattleshipHorizontal(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceRightMsg() }
}
// Downwards
DialogInterface.BUTTON_NEGATIVE -> {
try {
userInput = LEFT
placeBattleshipHorizontal(btnNum)
}
catch (e: IndexOutOfBoundsException) { noSpaceLeftMsg() }
}
}
}
val builder: AlertDialog.Builder? = AlertDialog.Builder(this)
builder?.setMessage("Place the battleship to the left or to the right?")
?.setPositiveButton("Right", dialogClickListener)
?.setNegativeButton("Left", dialogClickListener)?.show()
}
private fun placeBattleshipVertical(btnNum: Int) {
if(verticalCheck(btnNum)) {
if(userInput == UPWARDS) placeVertically(btnNum - 8 * (numOfTiles - 1), btnNum + 8)
else placeVertically(btnNum, btnNum + 8 * numOfTiles)
changeInstruction()
}
}
private fun placeBattleshipHorizontal(btnNum: Int) {
if(checkHorizontal(btnNum)) {
if (userInput == RIGHT) placeHorizontally(btnNum, btnNum + numOfTiles)
else placeHorizontally(btnNum - numOfTiles + 1, btnNum + 1)
changeInstruction()
}
}
private fun verticalCheck(btnNum: Int): Boolean {
// Are the tiles above occupied?
if(userInput == UPWARDS) {
for (num in btnNum - (8 * (numOfTiles - 1)) until btnNum) {
if (num < 0) {
noSpaceUpwardsMsg()
return false
}
}
val occupied = isOccupiedVertically(btnNum - 8 * (numOfTiles - 1), btnNum + 8)
if(!occupied) occupiedMsg()
return occupied
}
else {
for(num in btnNum + 8 until btnNum + (8 * (numOfTiles - 1)) step 8) {
if(num > 64) {
noSpaceDownwardsMsg()
return false
}
}
val occupied = isOccupiedVertically(btnNum, btnNum + 8 * numOfTiles)
if(!occupied) occupiedMsg()
return occupied
}
}
private fun checkHorizontal(btnNum: Int): Boolean {
if(userInput == LEFT) {
for (num in btnNum - numOfTiles + 1 until btnNum + 1) {
// Checking if battleship might be placed off screen
if (num % 8 == 0 && num != btnNum) {
noSpaceLeftMsg()
return false
}
}
// Checking if battleships overlap
val occupied = isOccupiedHorizontally(btnNum - numOfTiles + 1, btnNum)
if(!occupied) occupiedMsg()
return occupied
}
else {
for (num in btnNum until btnNum + numOfTiles) {
// Right corner to the right
if (num % 8 == 0 && num != btnNum + numOfTiles - 1) {
noSpaceRightMsg()
return false
}
}
}
val occupied = isOccupiedHorizontally(btnNum + 1, btnNum + numOfTiles)
if(!occupied) occupiedMsg()
return occupied
}
/* If a button has a listener, he is not occupied. */
private fun hasListener(num: Int): Boolean = tiles[num - 1].hasOnClickListeners()
// Checks the tiles vertically, returns true if not occupied
private fun isOccupiedVertically(start: Int, end: Int): Boolean {
for(num in start until end step 8) { if(!hasListener(num)) return false }
return true
}
// Checks the tiles horizontally, returns true if not occupied
private fun isOccupiedHorizontally(start: Int, end: Int): Boolean {
for(num in start until end) { if(!hasListener(num)) return false }
return true
}
private fun changeInstruction() {
when(numOfTiles) {
4 -> instruction.text = getString(R.string.place_your_4_tile_battleship)
3 -> instruction.text = getString(R.string.place_your_3_tile_battleship)
2 -> instruction.text = getString(R.string.place_your_2_tile_battleship)
1 -> instruction.text = getString(R.string.Go)
}
}
// Placing battleship by marking its tiles
private fun placeVertically(start: Int, end: Int) {
for (num in start until end step 8) {
occupyTile(num)
}
numOfTiles--
placedShips++
}
// Placing battleship by marking its tiles
private fun placeHorizontally(start: Int, end: Int) {
val ship = Battleship(numOfTiles)
for(num in start until end) {
ship.buildShip(tiles[num - 1])
occupyTile(num)
}
battleships.add(ship)
numOfTiles--
placedShips++
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun occupyTile(num: Int) {
//battleships[placedShips].buildShip(tiles[num - 1])
tiles[num - 1].background = getDrawable(R.drawable.button_occupied_tile)
tiles[num - 1].setOnClickListener(null)
}
@SuppressLint("UseCompatLoadingForDrawables")
fun damageShip() {
val btn = Utils.chooseTarget()
println("Target is: $btn")
val tile = tiles[btn - 1]
val s: Battleship
var didMiss = true
for(ship in battleships) {
// Successful hit.
if(ship.contains(tile)) {
s = ship
tile.background = getDrawable(R.drawable.button_hit_tile)
s.removeTile()
s.getShip()[s.getTileInd(tile)].setOnClickListener(null)
// Ship destroyed
if(s.getNumOfTiles() == 0) {
var i = 0
while(i < s.getSize()) {
s.getShip()[i].background = getDrawable(R.drawable.button_destroyed_tile)
i++
}
battleships.remove(s)
// Game Done
if (battleships.size == 0) {
instruction.text = getString(R.string.Lose)
Utils.gameEnd = true
for (t in tiles) t.setOnClickListener(null)
}
}
if(!Utils.gameEnd) {
didMiss = false
Thread.sleep(200)
userTurn()
}
else return
break
}
}
// Missed hit
if(didMiss) {
// Missed hit
tile.background = getDrawable(R.drawable.button_missed_tile)
Thread.sleep(200)
userTurn()
}
}
// This class represents a battleship
class Battleship(private var tiles: Int) {
private var ship: ArrayList<Button> = arrayListOf()
//private var size = tiles
fun buildShip(btn: Button) = ship.add(btn)
fun getSize() = ship.size
fun getShip() = ship
fun removeTile(){ tiles-- }
fun getNumOfTiles() = tiles
fun getTileInd(btn: Button) = ship.indexOf(btn)
fun contains(btn: Button): Boolean {
for(i in 0 until this.getSize()) { if(ship[i] == btn) return true }
return false
}
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun startGame() {
// If all battleships have been placed.
if(instruction.text == getString(R.string.Go)) {
// If the game hasn't started yet.
if (!inSession) {
inSession = true
for (btn in 1 until tiles.size + 1) {
tiles[btn - 1].background = getDrawable(R.drawable.button_start_tile)
}
instruction.text = ""
start.visibility = View.INVISIBLE
userTurn()
}
else inSessionMsg()
}
else notReadyMsg()
}
private fun userTurn() {
Utils.userTurn = true
val intent = Intent(this, ComputerSide::class.java)
this.startActivity(intent)
}
override fun onResume() {
super.onResume()
if(inSession) damageShip()
}
}
ComputerSide:
class ComputerSide : AppCompatActivity() {
private val VERTICAL = 0
private val HORIZONTAL = 1
private val UPWARDS = 2
private val DOWNWARDS = 3
private val LEFT = 4
private val RIGHT = 5
private lateinit var grid: GridLayout
private var tiles: ArrayList<Button> = arrayListOf()
private var battleships: ArrayList<Battleship> = arrayListOf()
private var numOfTiles = 5 // Number of tiles of the current battleship to be placed.
private var placedShips = 0
private lateinit var instruction: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_computer_side)
instruction = findViewById(R.id.instruction)
// Retrieving the game board
grid = findViewById(R.id.gridLayout)
for(btn in grid) {
tiles.add(findViewById(btn.id))
}
// Initializing battleships array
// for(num in 0 until 4) {
// battleships.add(Battleship(numOfTiles - num))
// }
// Adding a listener to all the tiles
for(btn in 1 until tiles.size + 1) {
tiles[btn - 1].setOnClickListener {}
}
placeBattleship()
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun placeBattleship() {
vertOrHor()
startGame()
}
private fun vertOrHor() {
// Vertical or Horizontal
while(numOfTiles > 1) {
val choice = Random.nextInt(VERTICAL,HORIZONTAL + 1)
when (choice) {
// Vertical
0 ->
try {
upOrDown()
}
catch (e: IndexOutOfBoundsException) { }
// Horizontal
1 ->
try {
leftOrRight()
}
catch (e: IndexOutOfBoundsException) { }
}
}
}
private fun upOrDown() {
val choice = Random.nextInt(UPWARDS,DOWNWARDS + 1)
placeBattleshipVertical(Utils.chooseStartingPlacement(), choice)
}
private fun leftOrRight() {
val choice = Random.nextInt(LEFT, RIGHT + 1)
placeBattleshipHorizontal(Utils.chooseStartingPlacement(), choice)
}
private fun placeBattleshipVertical(btnNum: Int, dir: Int) {
if(verticalCheck(btnNum, dir)) {
if(dir == UPWARDS) placeVertically(btnNum - 8 * (numOfTiles - 1), btnNum + 8)
else placeVertically(btnNum, btnNum + 8 * numOfTiles)
}
}
private fun placeBattleshipHorizontal(btnNum: Int, dir: Int) {
if(dir == RIGHT) {
// If there's space to the right
if(checkHorizontal(btnNum, dir)) placeHorizontally(btnNum, btnNum + numOfTiles)
else throw IndexOutOfBoundsException()
}
else {
// If there's space to the left
if(checkHorizontal(btnNum, dir)) placeHorizontally(btnNum - numOfTiles + 1, btnNum + 1)
else throw IndexOutOfBoundsException()
}
}
private fun verticalCheck(btnNum: Int, dir: Int): Boolean {
// Are the tiles above occupied?
if(dir == UPWARDS) {
for (num in btnNum - (8 * (numOfTiles - 1)) until btnNum) {
if (num < 0) return false
}
return (isOccupiedVertically(btnNum - 8 * (numOfTiles - 1), btnNum + 8))
}
else {
for(num in btnNum + 8 until btnNum + (8 * (numOfTiles - 1)) step 8) {
if(num > 64) return false
}
return (isOccupiedVertically(btnNum, btnNum + 8 * numOfTiles))
}
}
private fun checkHorizontal(btnNum: Int, dir: Int): Boolean {
if(dir == LEFT) {
for (num in btnNum - numOfTiles + 1 until btnNum + 1) {
// Checking if battleship might be placed off screen
if (num % 8 == 0 && num != btnNum) return false
// Checking if battleships overlap
return (isOccupiedHorizontally(btnNum - numOfTiles + 1, btnNum))
}
}
else {
for (num in btnNum until btnNum + numOfTiles) {
// Right corner to the right
if (num % 8 == 0 && num != btnNum + numOfTiles - 1) return false
}
}
return (isOccupiedHorizontally(btnNum + 1, btnNum + numOfTiles))
}
/* If a button has a listener, he is not occupied. */
private fun hasListener(num: Int): Boolean = tiles[num - 1].hasOnClickListeners()
// Checks the tiles vertically, returns true if not occupied
private fun isOccupiedVertically(start: Int, end: Int): Boolean {
for(num in start until end step 8) { if(!hasListener(num)) return false }
return true
}
// Checks the tiles horizontally, returns true if not occupied
private fun isOccupiedHorizontally(start: Int, end: Int): Boolean {
for(num in start until end) { if(!hasListener(num)) return false }
return true
}
// Placing battleship by marking its tiles
private fun placeVertically(start: Int, end: Int) {
val ship = Battleship(numOfTiles)
for (num in start until end step 8) {
ship.buildShip(tiles[num - 1])
occupyTile(num)
}
battleships.add(ship)
numOfTiles--
placedShips++
}
// Placing battleship by marking its tiles
private fun placeHorizontally(start: Int, end: Int) {
val ship = Battleship(numOfTiles)
for(num in start until end) {
ship.buildShip(tiles[num - 1])
occupyTile(num)
}
battleships.add(ship)
numOfTiles--
placedShips++
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun occupyTile(num: Int) {
//battleships[placedShips].buildShip(tiles[num - 1])
tiles[num - 1].background = getDrawable(R.drawable.button_occupied_tile)
tiles[num - 1].setOnClickListener(null)
}
@SuppressLint("UseCompatLoadingForDrawables")
fun damageShip(btn: Button) {
if(Utils.userTurn) {
var didMiss = true
val s: Battleship
for (ship in battleships) {
// Successful hit.
if (ship.contains(btn)) {
s = ship
btn.background = getDrawable(R.drawable.button_hit_tile)
s.getShip()[s.getShip().indexOf(btn)].setOnClickListener(null)
s.removeTile()
// Ship destroyed
if (s.getNumOfTiles() == 0) {
var i = 0
while (i < s.getSize()) {
s.getShip()[i].background = getDrawable(R.drawable.button_destroyed_tile)
i++
}
battleships.remove(s)
// Game Done
if (battleships.size == 0) {
instruction.text = getString(R.string.Win)
Utils.gameEnd = true
for (tile in tiles) tile.setOnClickListener(null)
}
}
if(!Utils.gameEnd) {
didMiss = false
Thread.sleep(200)
computerTurn()
}
else return
break
}
}
if(didMiss) {
// Missed hit
btn.background = getDrawable(R.drawable.button_missed_tile)
Thread.sleep(200)
computerTurn()
}
}
}
// This class represents a battleship
class Battleship(private var tiles: Int) {
private var ship: ArrayList<Button> = arrayListOf()
//private var size = tiles
fun buildShip(btn: Button) = ship.add(btn)
fun getSize() = ship.size
fun getTile(i: Int) : Button = ship[i]
fun getShip() = ship
fun removeTile() = tiles--
fun getNumOfTiles() = tiles
fun contains(btn: Button): Boolean {
for(i in 0 until this.getSize()) { if(ship[i] == btn) return true }
return false
}
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun startGame() {
for (tile in 1 until tiles.size + 1) {
tiles[tile - 1].background = getDrawable(R.drawable.button_start_tile)
tiles[tile - 1].setOnClickListener { damageShip(tiles[tile - 1]) }
}
Utils.initArray()
instruction.text = getString(R.string.strike_a_target)
}
private fun computerTurn() {
Utils.userTurn = false
val intent = Intent(this, UserSide::class.java)
this.startActivity(intent)
}
}
Is private fun vertOrHor()
running forever? How do you break out of this for loop?
The for loops in your onCreate
of ComputerSide
are doing too much stuff.
However, you need something to do those things.
You do not need a clickListener on every single button. You can put a single clickListener on the grid like how to use onclicklistener for grid view
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.