I am making tic tac toe in android studio on kotlin, I am new at this, I complete everything but i want to make that if none of combinations matchs then write on screen that it's draw but i cant write some logic or i missed something:
this is draw function code:
private fun draw() {
if (!firstPLayer.contains(1) && !firstPLayer.contains(2) && !firstPLayer.contains(3)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(2) && !secondPLayer.contains(3)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(4) && !firstPLayer.contains(5) && !firstPLayer.contains(6)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(4) && !secondPLayer.contains(5) && !secondPLayer.contains(6)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(7) && !firstPLayer.contains(8) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(7) && !secondPLayer.contains(8) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(1) && !firstPLayer.contains(4) && !firstPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(4) && !secondPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(2) && !firstPLayer.contains(5) && !firstPLayer.contains(8)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(2) && !secondPLayer.contains(5) && !secondPLayer.contains(8)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(3) && !firstPLayer.contains(6) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(3) && !secondPLayer.contains(6) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(1) && !firstPLayer.contains(5) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(5) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(3) && !firstPLayer.contains(5) && !firstPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(3) && !secondPLayer.contains(5) && !secondPLayer.contains(7)) {
resultView.text = "Draw!"
}
}
so, I am saying that if there isn't this combinations of number in lists, write "draw" in resultView
but it shows draw on first click on any button.
why?
The following assumes that playerOne and playerTwo are List<Int>s containing which of the nine squares from 1-9 the players have placed marks in.
Any time you see that you're copy-pasting code more than once (like resultView.text = "Draw!" and all the similar-looking if statements), you should rethink the design to simplify it.
I would create a collection of all possible win conditions:
private val winConditions = listOf(
listOf(1, 2, 3),
listOf(4, 5, 6),
listOf(7, 8, 9),
listOf(1, 4, 7),
listOf(2, 5, 8),
listOf(3, 6, 9),
listOf(1, 5, 9),
listOf(3, 5, 7),
)
Then each time a player makes a mark, you can check if either player won. If neither player won and nine moves have been made, the game is a draw.
// In a function called right after a player makes a move:
when {
winConditions.any { playerOne.containsAll(it) } -> showPlayerOneWins()
winConditions.any { playerTwo.containsAll(it) } -> showPlayerTwoWins()
playerOne.size + playerTwo.size == 9 -> showGameIsDraw()
else -> startNextPlayerTurn()
}
To get more fancy, you could have it only check the player who just made a move. That might make sense if it was a complicated algorithm to determine the winner, but Tic Tac Toe is trivial so I think it's just simpler to simply check them both so you can use the same code without extra parameters.
There are better ways to do what you're doing, like in Tenfour04's answer, but your basic approach is fine! Hard work, but it works. It's just the logic doesn't exactly fit what you're trying to do.
So, I'm assuming firstPlayer is a list (or some other collection) that holds the square numbers that player1 has marked with their X or O, and the same thing for secondPlayer. And I think what you're doing is checking all the winning combinations, and if nobody has any of those, the game is a draw. Is that right?
Your first problem is what you're running into - if you click a button, and draw() gets called, it tells you it's a draw. How come? Well, think about what you're doing - you're checking if anyone has a win condition, which they don't, because it's the start of the game. So the result is "draw!"
So really, you can't call draw() at any time, because the game has four possible states: player1 win, player2 win, draw (which are all finished states) and in progress, which basically means none of the end states have been reached yet. You're checking for the win states, but assuming that if they don't match, you must be in the draw state.
So, you'll either need to call draw() when you know one of those end states has been reached (which you can't really know without checking), or you'll need to explicitly check for a draw state along with the win state checks. (One thing you could think about, is whether all the squares have been filled.)
The other thing is that your logic doesn't really handle the win states properly. Imagine player 2 has the top line filled. The first if confirms player 1 doesn't have that line. They don't of course, so it prints "draw!" If you think about it, at most one of those win states can match, so either every other check is going to pass and set the result text to "draw!"
Typically the way you'd handle this kind of thing is to have a boolean flag set to a default, and set it when a check "trips the alarm" if you like. You only set it one way, so once it flips from the default to being set, another check won't flip it back
// assuming a draw until we know otherwise
var draw = true
// checking if player 1 has WON
if (firstPLayer.contains(1) && firstPLayer.contains(2) && firstPLayer.contains(3)) {
// when we know someone's won, we can say things like it's not a draw, maybe record who won
draw = false
}
...
// set the text either way, ensuring it always displays the correct thing
resultView.text = if (draw) "draw!" else ""
notice I swapped the if condition to check if player 1 has won, not if they've not won. This way we're looking for the specific condition where we can set the flag. It's easier to handle "was this condition was ever met" than "were any of these conditions never met".
It depends on the person, but sometimes it's easier to work these things out on paper - write down the questions you need to ask, to check if someone's won, someone's lost, is it a draw or is the game still going. Have a think about it!
Related
This is my goal : user click on minus button the amount decrease by one and there is a if statement to not allow the amount go lower than 0 .
This is my Code :
var number = 0
view.text_amount.visibility = View.GONE
view.plus_btn.setOnClickListener {
if (number == 5) {
Toast.makeText(
requireContext(),
"Limit in order",
Toast.LENGTH_SHORT
).show()
} else {
view.text_amount.visibility = View.VISIBLE
number++
view.text_amount.text = number.toString()
}
}
view.minus_btn.setOnClickListener {
if (number <= 0) {
view.text_amount.visibility = View.GONE
} else {
number--
view.text_amount.text = number.toString()
}
}
there is problem with code : I don't want the amount be visible after getting to 0 . it's better experience when the amount is equal to 0 not be visible .
I think it has a simple solution but I can't see it .
do you have any idea ?
Your code works fine! If you want to make it simpler, there's a bit of repeated logic you could condense into one function that handles the situations:
fun adjustValue(amount: Int) {
val adjusted = number + amount
if (adjusted > 5) // show Toast
number = adjusted.coerceIn(0, 5)
view.text_amount.text = number.toString()
view.text_amount.visibility = if (number == 0) View.GONE else View.VISIBLE
}
view.minus_btn.setOnClickListener { adjustValue(-1) }
view.plus_btn.setOnClickListener { adjustValue(1) }
basically the idea is you work out the new value (I'm using a temporary variable so we never set number to an invalid value) and show whatever warnings you need to. Then the coerceIn line makes sure we lock it within the valid range of allowed values.
You could do if/else checks and only set the new value if it's a valid one, but sometimes it's simpler and shorter to just set things and then worry about the edge cases, so this is just an example of that!
Same thing for the TextView bit - it's easier to just set the value whatever it is, and then set whether it should be displayed or not. You could use if/else branches to look at the value and decide whether to set the text or not... but why make your life hard? We know the value's in the valid 0 to 5 range, we can hide it if it's 0 and show it otherwise... easy!
You could make the function take a Boolean instead, like plus: Boolean and then go val adjusted = number + if (plus) 1 else -1, but making it an Int means you could easily add a +10 button or whatever without adding any more code or any more validation logic, and it's not any more complicated to pass in -1 instead of false (arguably it's clearer!)
that's probably more than you were asking for but hopefully it's useful. If nothing else, the "just set the text and the visibility every time" approach is good and neat
Decrease the value of text_amount only if it contains a value greater than 0 and after that check again its value and if it is 0 then hide it:
view.minus_btn.setOnClickListener {
if (number > 0) {
number--
view.text_amount.text = number.toString()
if (number == 0) view.text_amount.visibility = View.GONE
}
}
I have two list, one with all possible devices and another with just few devices. I need pass final list with this condition:
if full list == one of the items in smaller list, make this item "active" too true, else leave it false.
I have no problem when working with full list >500 devices and small list >50, but when I have for example 2000 devices everything start to be too slow (on Google Pixel 2XL I need to wait about 6 seconds to job finish).
Question: how can I increase this loop speed?
What I have done so far:
devicesList.forEach { device ->
device.selected = false
items.forEach { it ->
if(it.id == device.id){
device.selected = true
}
}
But this is too slow for larger data
You can speed it up a bit by not using forEach, which uses an interator and instead use a for loop. You can also break once you locate your id, assuming they are unique
for (i in 0 until devicesList.size) {
val device = devicesList[i]
for (j in 0 until items.size) {
val item = items[j]
if (item.id == device.id) {
device.selected = true
break
}
}
}
Assuming your ids are unique, you could also make a duplicate of the items list and drop those that have been located, so each loop is shorter, like this
val copy = items.toMutableList()
for (i in 0 until devicesList.size) {
val device = devicesList[i]
for (j in 0 until copy.size) {
val item = copy[j]
if (item.id == device.id) {
device.selected = true
copy.remove(item)
break
}
}
}
You could also consider creating a map where the key is your id so you do not have to loop and instead you retrieve the item by id directly. You have to weight the cost of creating the map in the first place.
val map = items.associateBy { it.id }
for (i in 0 until devicesList.size) {
val device = devicesList[i]
device.selected = map[device.id] != null
}
Besides this, you should also move your logic to a background thread and wait for it to complete.
If all you need is make selected = true if a device's id exists in the list items, you can get all the ids of items like this:
val ids = items.map { it.id }
and then loop through devices:
devicesList.forEach { it.selected = it.id in ids }
My problem: I'm working on a Android (ArcGis) Attribute Editor following the sample code from their tutorials. The problem occurs when I have more than 1 ArcGisDynamicLayer and, more important, more than 1 ArcGisFeatureLayer.
My question: What is the (correct) approach to IDENTIFY the right Feature Layer to query when I tap on a "field" displayed on the map?
I read a similar question (Arcgis SDK for Android - Query multiple feature layers), but I didn't understand how to use IdentifyTask using only a single tap.
EDIT: I tried to get the graphics that I tapped on to compare it to the graphics from every feature layer. If they are equal, that's the feature layer that I'm looking for. But that's not working, because my feature layers don't seem to have any graphics.
mapView.setOnSingleTapListener(new OnSingleTapListener() {
public void onSingleTap(float x, float y) {
/*...construct query...*/
Graphic g = null;
for (Layer layer : mapView.getLayers()) {
if (layer instanceof ArcGISFeatureLayer) {
ArcGISFeatureLayer fLayer = (ArcGISFeatureLayer) layer;
// Get the Graphic at location x,y
final int[] ids = fLayer.getGraphicIDs(x, y, 10, 1);
if (ids != null && ids.length != 0) {
g = fLayer.getGraphic(ids[0]);
System.out.println("Graphic: " + g);
}
}
/*...loop through every FeatureLayer and verify like :...*/
for(...)
if(layer.getGraphic(g.getUid()) != null && && g.equals(layer.getGraphic(g.getUid()))) {
/* call the select features method and implement the callbacklistener */
/* rest of the story */
}
}
I've done my app and it works great on the Corona Simulator, but, once I run it on my device, it doesn't run a bunch of instruction.
These are:
function onCollision( event )
if ( event.phase == "began" ) then
if event.object1.myName == "ground" and event.object2.myName == "spaceShip" then
local a = score.get()
print(a)
local b = score.load()
print(b)
if (a < b) then
best.alpha = 1
scoreToBeat.text = score.load()
scoreToBeat.alpha = 1
else
score.save()
newRecord.alpha = 1
end
timer.cancel(tmrScore)
gameOver.alpha = 1
tapToReplay.alpha = 1
replay.alpha = 0.01
fade.alpha = 0
timer.cancel(tmrIS)
spaceShip.alpha = 1
if(playEffects) then
media.playEventSound( "sounds/gameover.mp3" )
playEffects = false
end
speed = 0
end
end
end
Runtime:addEventListener( "collision", onCollision )
Particularly, it performs only "speed = 0" and only the first time I run the app, which means that if I start a new game, it won't even work "speed = 0".
I'm 100% sure that the app is running on the device is the same that runs on the simulator (I tried to change some text).
What can I do?
There could be multiple reasons: the physics not started when get back to scene; the objects aren't dynamic; one of them no longer exists or is no longer a physics object; the handler was removed when game ended and not restarted.
To figure out, add some print statements to see what parts execute:
function onCollision( event )
print 'entered collision'
if ( event.phase == "began" ) then
print 'collision began'
if event.object1.myName == “gd” and event.object2.myName == “hero” then
print 'collision objects are gd and hero'
speed = 0
if (score.get() < score.load()) then
print 'score smaller'
...
else
print 'score not smaller'
...
end
else
print( 'collision objects are', event.object1.myName, event.object2.myName )
end
end
end
Update:
I had a typo score = 0, should have been speed = 0.
I'm new in Android games using corona and I use a timer to make a local display of coin with a 50x repetition.
What I'm trying to do is if the character collide in on of coin the coin will disappaer, the problem is how can I hide that certain coin?
here's my code how I creating the coin.
function coins()
coin1 = display.newImage( "coin1.png")
coin1.x = math.random(0, 600)
coin1.y = math.random(0, 400)
coin1.myName = "wewe"
physics.addBody(coin1, {friction = 1, density = 1})
end
timer.performWithDelay(
1000, coins, 100 )
have something like this
local function removeCoin(self,event)
if(event.phase == "began") then
self:removeSelf()
end
end
And in coins() add the following
coin1.collision = removeCoin
coin1:addEventListener("collision",coin1)
This should make it that upon a coin experiencing a collision removeCoin is invoked, which removes it's caller, in this case a coin.
You can stop both objected being removed by doing something like this:
if(event.phase == "began" and self.myName == 'coin') then
self:removeSelf()
end