Generate the same maze with a seed - android

Im followed a tutorial to create a maze with Recursive Backtracking and it works great.
Im trying to create a game where people get on the same maze, and if someone wins, it creates a new maze and everyones current maze gets updated.
So what i was thinking is to have a seed to create the same maze and pass that seed to all the players so they can have the same maze.
Is there a way to modify it so i can give the maze a seed and it creates always the same maze?
This is what i have now:
It uses a Cell class (posx,posy)
class Cell(var col:Int = 0, var row: Int = 0){
var topWall = true
var leftWall = true
var bottomWall = true
var rightWall = true
var visited = false
}
fun createMaze(){
var stack = Stack<Cell>()
var current:Cell
var next:Cell?
for(x in 0 until COLS){
for(y in 0 until ROWS){
cells[x][y] = Cell(x,y)
}
}
player = cells[0][0]
exit = cells [COLS-1][ROWS-1]
current = cells[0][0]
current.visited = true
do{
next = getNeighbour(current)
if(next != null) {
removeWall(current, next)
stack.push(current)
current = next
current.visited = true
}else{
current = stack.pop()
}
}while (!stack.empty())
}
fun getNeighbour(cell:Cell): Cell? {
var vecinos: ArrayList<Cell> = ArrayList()
//vecino izquierda
if(cell.col > 0) {
if (!cells[cell.col - 1][cell.row].visited) {
vecinos.add(cells[cell.col - 1][cell.row])
}
}
//vecino derecha
if(cell.col < COLS - 1) {
if (!cells[cell.col + 1][cell.row].visited) {
vecinos.add(cells[cell.col + 1][cell.row])
}
}
//vecino arriba
if(cell.row > 0) {
if (!cells[cell.col][cell.row - 1].visited) {
vecinos.add(cells[cell.col ][cell.row - 1])
}
}
//vecino abajo
if(cell.row < ROWS - 1) {
if (!cells[cell.col][cell.row + 1].visited) {
vecinos.add(cells[cell.col][cell.row + 1])
}
}
if (vecinos.size > 0) {
var index = random.nextInt(vecinos.size)
return vecinos[index]
}else {
return null
}
}
fun removeWall(current:Cell,next:Cell){
if (current.col == next.col && current.row == next.row +1){
current.topWall = false
next.bottomWall = false
}
if (current.col == next.col && current.row == next.row -1){
current.bottomWall = false
next.topWall = false
}
if (current.col == next.col + 1 && current.row == next.row){
current.leftWall = false
next.rightWall = false
}
if (current.col == next.col - 1 && current.row == next.row){
current.rightWall = false
next.leftWall = false
}
}

If you want to pass a seed to create the maze, then you have to make sure that all of the players are using the same random number generator. Which means you have to supply your own random number generator implementation.
The application would seed the random number generator with the value you pass, and then it should deterministically generate the same sequence of random numbers for each client.
Note also that you can't ever change the random number generator implementation unless you can prove that the new implementation will generate exactly the same sequence of numbers that the original did.

Related

I am trying to make TicTacToe Application

I am trying to make TicTacToe Application, I have already implemented the "player Vs player" part but I am having trouble with implementing the player Vs computer. I have a function called playgame. For update_player I am doing manually and for update computer, I am doing it using random, and I think this is causing the issue as I am checking if my boardStatus is already filled, if it's filled I am calling my function again. I read online that all the calculation should be done on thread, I tried implementing it but I think I am doing it wrong. Please Help!
Here's my code for reference:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import kotlinx.android.synthetic.main.activity_computer.*
import java.util.*
class ComputerActivity : AppCompatActivity() {
var player = true
var turnCount = 0
var boardStatus = Array(3) { IntArray(3) }
lateinit var board: Array<Array<Button>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_computer)
board = arrayOf(
arrayOf(First, Second, Third),
arrayOf(Fourth, Fifth, Sixth),
arrayOf(Seventh, Eighth, Ninth)
)
playGame()
}
private fun playGame(){
Status.text = "Player's Turn"
for (i in 0..2) {
var flag: Boolean = false
for (j in 0..2) {
if (player) {
Status.text = "Player's Turn"
update_player(player)
Thread.sleep(2000)
player = false
turnCount++
checkWinner()
if (turnCount == 9) {
Status.text = "Game Draw"
flag = true;
}
} else {
Status.text = "Computer's Turn"
update_computer(player)
Thread.sleep(2000)
player = true
turnCount++
checkWinner()
if (turnCount == 9){
Status.text = "Game Draw"
flag = true
}
}
}
if(flag == true)
break
}
changeBoard()
resetBtn.setOnClickListener{
player = true;
turnCount = 0
changeBoard()
}
}
private fun update_player(player:Boolean){
for(i in board){
for(button in i){
button.setOnClickListener{
when(it.id){
R.id.First->{
updateBoardStatus(row = 0, column = 0,player)
}
R.id.Second->{
updateBoardStatus(row = 0, column = 1,player)
}
R.id.Third->{
updateBoardStatus(row = 0, column = 2,player)
}
R.id.Fourth->{
updateBoardStatus(row = 1, column = 0,player)
}
R.id.Fifth->{
updateBoardStatus(row = 1, column = 1,player)
}
R.id.Sixth->{
updateBoardStatus(row = 1, column = 2,player)
}
R.id.Seventh->{
updateBoardStatus(row = 2, column = 0,player)
}
R.id.Eighth->{
updateBoardStatus(row = 2, column = 1,player)
}
R.id.Ninth->{
updateBoardStatus(row = 2, column = 2,player)
}
}
}
}
}
}
private fun update_computer(player:Boolean){
var row:Int = 0
var column:Int = 0
Thread {
row = (0..2).random()
column = (0..2).random()
}.start()
if(boardStatus[row][column] == 0 || boardStatus[row][column]==1)
update_computer(player)
else
updateBoardStatus(row, column, player)
}
private fun updateBoardStatus(row:Int, column:Int, player:Boolean){
val text = if (player) "X" else "0"
val value = if (player) 1 else 0
board[row][column].apply {
isEnabled = false
setText(text)
}
boardStatus[row][column] = value
}
private fun checkWinner(){
//Horizontal --- rows
for (i in 0..2) {
if (boardStatus[i][0] == boardStatus[i][1] && boardStatus[i][0] == boardStatus[i][2]) {
if (boardStatus[i][0] == 1) {
result("Player Won!!")
break
} else if (boardStatus[i][0] == 0) {
result("Computer Won")
break
}
}
}
//Vertical --- columns
for (i in 0..2) {
if (boardStatus[0][i] == boardStatus[1][i] && boardStatus[0][i] == boardStatus[2][i]) {
if (boardStatus[0][i] == 1) {
result("Player Won!!")
break
} else if (boardStatus[0][i] == 0) {
result("Computer Won!!")
break
}
}
}
//First diagonal
if (boardStatus[0][0] == boardStatus[1][1] && boardStatus[0][0] == boardStatus[2][2]) {
if (boardStatus[0][0] == 1) {
result("Player Won!!")
} else if (boardStatus[0][0] == 0) {
result("Computer won!!")
}
}
//Second diagonal
if (boardStatus[0][2] == boardStatus[1][1] && boardStatus[0][2] == boardStatus[2][0]) {
if (boardStatus[0][2] == 1) {
result("Player Won!!")
} else if (boardStatus[0][2] == 0) {
result("Computer Won!!")
}
}
}
private fun result(res:String){
Status.text = res
if(res.contains("Won")){
disableButton()
}
else{
}
}
private fun disableButton(){
for(i in board){
for(button in i){
button.isEnabled = false
}
}
}
private fun changeBoard(){
for (i in 0..2) {
for (j in 0..2) {
boardStatus[i][j] = -1
}
}
for (i in board) {
for (button in i) {
button.isEnabled = true
button.text = ""
}
}
}
}
Your code is trying to put the whole sequence of actions of the game in a function that is called once and then expects player button-presses to happen internally. Button listeners will fire some time in the future, after the function already returns. You need to think in terms of there being a function that is called each time a button is pressed to do the next stage of the game.
To fix this:
Remove the playGame() function.
Remove the player parameter from update_player() since it's always true. And change the function name to initializeButtons. Call it once in onCreate(). You only have to add listeners to the buttons one time and they will work repeatedly.
Also remove the player parameter from update_computer() for the same reason as above. And remove the threading so it looks like:
private fun update_computer() {
val row = (0..2).random()
val column = (0..2).random()
if (boardStatus[row][column] == 0 || boardStatus[row][column] == 1)
update_computer()
else
updateBoardStatus(row, column, player)
}
Then at the end of the updateBoardStatus function call checkWinner(). checkWinner() should return a Boolean, so in updateBoardStatus(), if no win condition has been found and player is true, it should call update_computer().
So what you have now instead of trying to run the game from one function, you set up button listeners one time to start the game. When a button is pressed, it takes the player turn, which then triggers updateBoardStatus, which then triggers the computer turn, which then triggers updateBoardStatus again, and then does nothing if no one won. All of that happens synchronously/instantly on the main thread, so now the game is back to waiting for a button press from the user to repeat the sequence of events.
Also, the status text view has limited usefulness. Since the computer takes its turns instantly, it's not possible to ever see the words "Computer's turn". If you want to do that, you'll have to create an artificial delay, so you would have to disable all the buttons and then do something like call postRunnable({ startPlayerTurn() }, 1000L), where the startPlayerTurn() re-enables the appropriate buttons and makes it say, "Player turn" again.

Kotlin if statement fails

I have following if statement but it always fails to show correct data
if(!currentItem.address.toString().isNullOrEmpty() && !useraddress.isNullOrEmpty()) {
holder.distanceca.isVisible = true
} else {
holder.distanceca.isVisible = false
}
Explanation
Based on my sample data useraddress is null so it suppose to fall into holder.distanceca.isVisible = false but instead it's returning holder.distanceca.isVisible = true
PS: for my purpose of running holder.distanceca.isVisible = true both currentItem.address and useraddress must have values if any of them is empty or null it should hide the element.
Any idea how to properly make if statement in kotlin?
Solved
Working code
if(!currentItem.address.isNullOrEmpty() && !useraddress.isNullOrEmpty()) {
val geoCoder = Geocoder(context)
// laundry location
val arrs1 = geoCoder.getFromLocationName(currentItem.address, 1)
if (arrs1?.isNotEmpty() == true) {
address = arrs1[0]
}
// customer location
val arrs = geoCoder.getFromLocationName(useraddress, 1)
if (arrs?.isNotEmpty() == true) {
usera = arrs[0]
}
if (arrs1?.isNotEmpty() == true && arrs?.isNotEmpty() == true) {
holder.distanceca.isVisible = true
val locationA = Location(currentItem.name)
locationA.latitude = address?.latitude ?: 0.0
locationA.longitude = address?.longitude ?: 0.0
val locationB = Location("You")
locationB.latitude = usera?.latitude ?: 0.0
locationB.longitude = usera?.longitude ?: 0.0
val distance = DecimalFormat("##.##").format(locationA.distanceTo(locationB) / 1000)
holder.distanceca.text = "${distance} KM"
} else {
holder.distanceca.isVisible = false
}
}
I've add extra if condition to my code and that fixed it somehow (honestly, I am not sure why is that myself :D )
if (arrs1?.isNotEmpty() == true && arrs?.isNotEmpty() == true) {
//
}
Try doing this:
if(!currentItem.address.toString().isNullOrEmpty()) {
useraddress?.let{
if (!it.isNullOrEmpty())
holder.distanceca.isVisible = true
else
holder.distanceca.isVisible = false
}?.let{
holder.distanceca.isVisible = false
}
}

Android Multi-row summation: Request for code shortening

I have a table with fifteen rows. Each row have three columns and a total column. I want to get the total per row, the grand total, and the overall average.
The user may not enter data for all rows, and the user may skip a row.
So the code checks if the user have entered data in one of three fields of each row.
If the row is blank, ignore it.
If some of the fields are filled-up, tell the user to fill up the rest of the row.
If all the fields in a row is filled up, sum all its fields and increment the divider.
I have only pasted the codes for Rows 1 & 2 for brevity, but it shows the gist of what I'm trying to achieve:
The code:
var a1 = 0
var a2 = 0
var total = 0
var divider = 0
// Row 1
if (b1p1.text.isNotEmpty() or b2p1.text.isNotEmpty() or b3p1.text.isNotEmpty()) {
var y = 0
listOf(b1p1, b2p1, b3p1).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p1, b2p1, b3p1).forEach {
a1 += it.text.toString().toInt()
}
total1.text = a1.toString()
total += a1
e2 = 1
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
// Row 2
if (b1p2.text.isNotEmpty() or b2p2.text.isNotEmpty() or b3p2.text.isNotEmpty()) {
var y = 0
listOf(b1p2, b2p2, b3p2).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p2, b2p2, b3p2).forEach {
a2 += it.text.toString().toInt()
}
total2.text = a2.toString()
total += a2
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
if (e2 == 1) {
grandTotalTextView.text = total.toString()
average = total.toDouble()/divider
val decimalFormatter = DecimalFormat("#,###.##")
averageTextView.text = decimalFormatter.format(average).toString()
cyeSingleton.anct3b = decimalFormatter.format(average).toString()
} else {
Toast.makeText(activity, "Error 2", Toast.LENGTH_SHORT).show()
}
The table:
This is the best I could come up with. Should there be no other suggestion, I will settle for this.
Thanks in advance!
**EDIT: Thanks to ** https://stackoverflow.com/users/3736955/jemshit-iskenderov
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
private fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>): TotalResult {
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews as List<EditText>, totalTextViews[index])
if(!rowResult.ignoreRow){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
Toast.makeText(
activity,
"$divider, $allTotal, $showError", Toast.LENGTH_SHORT)
.show()
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
private fun calculateRowResult(rowTextViews:List<EditText>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.text.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
fun main(){
val totalResult = calculateTotalResult(
allTextViews = listOf(
listOf(t11,t12,t13),
listOf(t21,t22,t23)
),
totalTextViews = listOf(totalView1, totalView2)
)
// single Toast error
if(totalResult.showError){
// showToast(error)
}
// use totalResult.divider, totalResult.allTotal
}
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>){
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews, totalTextViews[index])
if(!rowResult.ignore){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
fun calculateRowResult(rowTextViews:List<TextView>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
Extracted calculateTotalResult() and calculateRowResult() so multiple rows and columns do not need to repeat same code.
calculateRowResult() processes singlet row of TextViews. I had to iterate rowTextViews twice, one to calculate ignore, the other to show error on TextView if not ignore. We don't show Toast Error here yet.
calculateTotalResult() iterates through all rows and gets total result. We show only one Toast Error (if required) after this step.
Code is pseudo-code, not tested.

How to proceed coroutine work if object state was changed?

I try to create an android game on pure kotlin, but I stacked on the bullet move rendering. If I do single shoot and will waiting that it disappear it works well, but if I moves player and do the next one shoot, previous shoot freeze on their position and doesn't finish their rendering. It looks like this:
On the picture: shooted once, moved left on the one point and do another shoot
Seems that the problem is in my drawShoot method. If the player position changed , that shoot position changed too, but I expect that coroutines will solved my problem, but it's isn't. Here is my code, any ideas hot to fix the bug?
Here is my code :
class GameV2 {
companion object {
private const val BLANK_CHAR = " "
private const val BLANK_CHAR_CODE = 0
private const val AREA_BORDER_CHAR = "x"
private const val AREA_BORDER_CHAR_CODE = 1
private const val PLAYER_ONE = "^"
private const val PLAYER_ONE_CODE = 2
private const val SHOOT = "o"
private const val SHOOT_CODE = 3
private var gameFinished = false
lateinit var gameAreaWidth: Number
lateinit var gameAreaHeigth: Number
private var gameArea = Array(0) { IntArray(0) }
private val observer = GameStateObserver()
private val gameState = GameState()
var shootPositionY: Int = 0
var shootPositionX: Int = 0
var playerPositionX: Int = 0
var playerPositionY: Int = 0
var bulletPlayerOnePositionX: Int = 0
var bulletPlayerOnePositionY: Int = 0
var bulletPlayerTwoPositionY: Int = 0
var bulletPlayerTwoPositionX: Int = 0
fun prepareGameEngine() {
observer.addObserver(gameState)
observer.changeState()
}
fun initGameArea(x: Int, y: Int) {
gameAreaWidth = y
gameAreaHeigth = x
gameArea = Array(x) { IntArray(y) }
var i = 1
var j: Int
while (i <= x) {
j = 1
while (j <= y) {
if (i == 1 || i == x || j == 1 || j == y) {
gameArea[i - 1][j - 1] = 1
} else {
gameArea[i - 1][j - 1] = 0
}
j++
}
i++
}
}
fun drawGameArea(): String {
val sb = StringBuffer()
gameArea.forEach { i ->
//println()
sb.append("\n")
i.forEach { j ->
if (j == BLANK_CHAR_CODE) {
// print(BLANK_CHAR)
sb.append(BLANK_CHAR)
}
if (j == AREA_BORDER_CHAR_CODE) {
// print(AREA_BORDER_CHAR)
sb.append(AREA_BORDER_CHAR)
}
if (j == PLAYER_ONE_CODE) {
// print(PLAYER_ONE)
sb.append(PLAYER_ONE)
}
if (j == SHOOT_CODE) {
// print(SHOOT)
sb.append(SHOOT)
}
}
}
return sb.toString()
}
private fun clearGameAreaSpaceInCoords(x: Int, y: Int) {
gameArea[x][y] = BLANK_CHAR_CODE
}
fun updateUserPosition(x: Int, y: Int) {
playerPositionX = x
playerPositionY = y
gameArea[playerPositionX][playerPositionY] = PLAYER_ONE_CODE
observer.changeState()
}
fun moveUserRight() {
if (playerPositionY < gameAreaWidth.toInt() - 2) {
// example: y - 1 = move left; x - 1 = move up
clearGameAreaSpaceInCoords(playerPositionX, playerPositionY)
updateUserPosition(playerPositionX, playerPositionY + 1)
}
}
fun moveUserLeft() {
if (playerPositionY > 1) {
clearGameAreaSpaceInCoords(playerPositionX, playerPositionY)
updateUserPosition(playerPositionX, playerPositionY - 1)
}
}
fun drawShoot() {
// playerPositionX - 1 mean that 1 point higher than player character
shootPositionY = playerPositionY
shootPositionX = playerPositionX
GlobalScope.launch { // launch a new coroutine in background and continue
for(i in 1..gameAreaHeigth.toInt() - 3 ){
gameArea[shootPositionX - i][shootPositionY] = SHOOT_CODE
observer.changeState()
delay(300L)
clearGameAreaSpaceInCoords(shootPositionX - i, shootPositionY)
observer.changeState()
}
}
}
private fun isGameStateChanged(): Boolean {
return GameStateObserver().hasChanged()
}
fun startGame() {
}
}
}
SOLVED. Solution : It's my bad. I call global variable inside drawShoot method. All what i need it's change global var to local, like this:
fun drawPlayerOneShoot() {
// playerOnePositionX - 1 mean that 1 point higher than player character
val shootPositionY = playerOnePositionY
val shootPositionX = playerOnePositionX
// launch a new coroutine in background and continue
GlobalScope.launch {
for(i in 1..gameAreaHeigth.toInt() - 3 ){
gameArea[shootPositionX - i][shootPositionY] = SHOOT_CODE
observer.changeState()
delay(300L)
clearGameAreaSpaceInCoords(shootPositionX - i, shootPositionY)
if (shootPositionX - i == playerTwoPositionX && shootPositionY == playerOnePositionY){
isPlayerOneWin = true
println("PLAYER ONE WIN")
}
observer.changeState()
}
}
}
From the first glimpse it seems that the problem lies not in the Coroutine itself, but inside of observer.changeState() . What kind of code is there? I guess it uses a singular "shot" instance and it does not matter whether you launch a new coroutine to change it.

kotlin firebase multiple loop

when I add multiple loop to retrieve data from firebase only the first loop works, why is that happening and how can I make all the loops work
val children = dataSnapshot!!.children
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(0).toString())){
//
}
}
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(1).toString())){
//
}
}
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(2).toString())){
//
}
}
At first glance the 3 loops look identical, but they differ at the last 3d of the condition. Each one checks this:
it.child("location").getValue().toString().equals(items.get(x).toString())
where x is 0, 1 or 2.
So they are not the same as to be surprised why the last 2 don't work as you say.
Maybe the condition works only for:
items.get(0).toString()
Anyway you can simplify your code and add some debugging code so to be sure what's happening:
(0..2).forEach { i ->
children.forEach {
val dateOk = it.child("Date").getValue().toString().equals(Data.date)
val timeOk = it.child("Time").getValue().toString().equals(Data.time)
val itemOk = it.child("location").getValue().toString().equals(items.get(i).toString())
Log.d("Loop", "at $i: dateOk = $dateOk, timeOk = $timeOk, itemOk = $itemOk")
if( dateOk && timeOk && itemOk){
when (i) {
0 -> //
1 -> //
2 -> //
}
}
}
}

Categories

Resources