How to get an Int input from user in Kotlin - kotlin

I am currently creating an othello game in Kotlin. I have used Kotlin before in a robotics format, where I really didn't need to display text. Now that I am creating a user Int input to place a piece, I'm not really sure what to do. I've looked everywhere and kept on getting errors. Does anyone know how to do this? (function where I do the input is down below) the rest of the code is here : https://github.com/Spanini2/othello
fun main() {
var board = othelloBoard
var cPlayer = 'X'
setupBoard(board)
while (true) {
showBoard(board)
println("It is player $cPlayer 's turn")
println("Enter coords of move:")
var xInput = readLine()!!.toInt()
var yInput = readLine()!!.toInt()
placePiece(board, xInput, yInput, cPlayer)
}
}

Related

Lazycolumn with lazyrow that are loaded with api call

i'am new to jetpack compose and i would like to achieve a view but i'am really struggling with it. it a catalogue movie where you can chose film in different categories, so you get the all the categories listed in column and for each category you get multiples films in the row.
Here is what i want to do : i have a viewmodele to make call to my api. So i make a first call giving me the list of categories. then for each category have to make an other call of my view modele to get films of this category. Here is how i started :
class CatalogViewModel() : ViewModel() {
var loadCategories by mutableStateOf(false)
var categories: ArrayList<Category> = arrayListOf()
init {
fetchCategories()
}
fun fetchCategories() {
this.viewModelScope.launch {
Api.Call(
completionHandler = object : completionHandler<ArrayList<Category>, Error> {
override fun succeed(result: ArrayList<Category>?) {
loadCategories = true
if (result != null) {
categories = result
loadCategories = true
}
}
override fun failed(error: PKError?) {
println("failed")
loadCatalog = false
}
})
}
}
completionHandler is just and interface that we use to make our api call :
interface completionHandler<in T, in U> {
fun succeed(result: T?)
fun failed(error: U?)}
So first of all i don't really know how states work in jetpack compose so i'am not sure if i'am doing it good or not but it's work, now in my composable i just have to check
if (CatalogViewModel.loadCategories)
Then i can display my list of categories. But here is where i don't know how to do further : now for each category i have to make an other call to get the film that i need to display is the row, but i can't use the same technique because i'am not sur how much call i'll need to do, so i can't have a mutable state of boolean to observe in order to compose my row. So how can i do a call for each row and compose when the call is done ? also this has to be asynchrone because i can't wait all row to be loaded to display them.
And in a second time, i'll maybe get a lot of categories so i would like to only load those displayed on the screen.
I feel like i'am doing something wrong or maybe i didn't really get how to work with jetpack compose. So i would really like some help, thanks

Take snapshot of MutableStateFlow that doesn't change with the state in Jetpack Compose

I'm trying to make an editor app that allows you to undo/redo changes. I want to achieve that by storing the ui state in stacks (ArrayDeque) and pop them back once the user hits undo/redo. But every time I stored the state in stack, after I made change to the state, the value in the stack is changed as well.
Is there a way to snapshot a state that won't be affected by future changes in the state flow?
My code looks something like this:
Data Class
class Data () {
var number: Int = 0
fun foo()
}
State Class
data class UiState(
val dataList: MutableList<Data> = mutableListOf(),
)
ViewModel
class UiViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
private val undoStack: ArrayDeque<UiState> = ArrayDeque()
fun makeChangeToState() {
saveStateToUndoStack(_uiState.value)
Log.i("Test", undoStack.last().dataList[0].number) // 0
val dataList= _uiState.value.dataList.toMutableStateList()
dataList[0].number = 1
_uiState.update { currentState ->
currentState.copy(
dataList = dataList,
)
}
Log.i("Test", undoStack.last().dataList[0].number) // 1 because the _uiState changed
}
fun undo() {
val lastState = undoStack.last()
// Won't work because the data in lastState has already been updated with _uiState
_uiState.update { lastState }
}
}
Things I've tried:
Use _uiState.value.copy
Call saveStateToUndoStack(uiState: UiState) from Composable functions and pass in the viewModel.uiState.collectAsState()
Both doesn't seem to work, I play around for a few hours but don't have a clue.
The reason why the old value got updated is because all the objects in the list are references, not related to MutableStateFlow. I just need a deep copy of the list, see this post here:
https://www.baeldung.com/kotlin/deep-copy-data-class
Another thread worth reading: Deep copy of list with objects in Kotlin

Trying to get a when expression to show the result on the ui in kotlin

I have been stuck making this metric converter app for over a week I keep going back to the previous codelabs that explain how to make a tip calculator but when I try to apply the same method to this app that I'm trying to make it doesn't have any errors but the app crashes whenever I try to test it to see if I can get the functionality to work this is my last resort. I've tried to do when statements but I kept getting the error that certain branches would never be reached. if anyone can point me in the right direction as to what I'm doing wrong or what's missing from my code. I'm all ears.
package com.example.metricconversion
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.metricconversion.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
setContentView(binding.root)
binding.conversionTo.setOnClickListener { convertMetric() }
}
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// val grainToQtr = R.id.Grain and R.id.quarter1
//val grainToSt= R.id.Grain and R.id.quarter1
//val grainToLb = R.id.Grain and R.id. pound1
//val grainToTon= R.id.Grain and R.id.Ton1
val st = R.id.Grain and R.id.stone1
// code to convert grain to the other options
//code to convert grain to the other options
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ounce1 -> {
amountIQ / 437.5
}
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.quarter1 -> amountIQ * 0.000005714
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.pound1 -> amountIQ * 0.0001429
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ton1 -> amountIQ / 1.4e+7
}
when (selectedId and selectedId2) {
st -> {
amountIQ * 0.0000102
}
}
binding.metricConverted.text=getString(R.string.end_result,convertMetric())
}
}
Problems with your code:
Using and to check two values for equality at the same time is not viable. The and function does a bitwise operation to merge the two numbers together. There are many possible inputs that could merge to the same solution, so you will get false positives. Instead you could use the to infix function to combined the two numbers into a Pair wrapper object so they are both preserved for the comparison.
You have a series of individual when statements, each with only a single condition. It doesn't make sense to use a when statement with a single condition. Usually a single condition would be represented with an if statement instead of when statement. But I'm guessing you didn't mean for these to be separate when statements, based on what I'm seeing.
Your when statements are not actually doing anything. Their branches resolve to a number, but you're not doing anything with that number (not assigning it to a variable or logging it or showing it in the UI, etc.). So they are useless. Your statement at the bottom isn't getting the result of any of the when statements, but is instead recursively calling the same function again. This function doesn't return anything, so that's useless. Even if it did return a number, the recursive call will create an infinite loop, resulting in a stack overflow.
So first, to just fix your code (we can discuss a better way of doing it later):
We replace and with to.
We merge all the when statements into a single statement.
We store the result of the when statement in a variable and use that variable to set the text in the last line of the function. When you use a when statement with a subject (something in parentheses that is compared for each branch) or to get a result, you have to cover every possible case, so an else branch must also be added.
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// code to convert grain to the other options
val result = when (selectedId to selectedId2) {
R.id.Grain to R.id.Ounce1 -> amountIQ / 437.5
R.id.Grain to R.id.quarter1 -> amountIQ * 0.000005714
R.id.Grain to R.id.pound1 -> amountIQ * 0.0001429
R.id.Grain to R.id.Ton1 -> amountIQ / 1.4e+7
R.id.Grain to R.id.stone1 -> amountIQ * 0.0000102
else -> error("Unsupported selection pair")
}
binding.metricConverted.text = getString(R.string.end_result, result.toString())
}
Above, the error() call will crash your app. You need to make sure you cover every possible combination that could occur. During development, this is suitable, but for production you might want to change the behavior so it shows an error message in the UI of the app and doesn't crash.
Now, regarding the overall design, it is quite fragile because you have UI layout details so tightly coupled to your app's behavior. All these formula calculations should probably be defined in a separate class, possibly an Enum class. You could create a Map in your Activity file that links the UI elements to the behavior class(es), and then in your when statement, you could use the UI elements to pull the associated behavior from the map. This would be more maintainable, and make it easier for you to avoid forgetting something as you add/modify functionality. I say this just to get you thinking about it, but it's probably too much for a beginner project right now. I don't have time to explain in detail how I would do all of that for your case.

Why add method overwrites all items in array list

I have board for game tic-tac-toe. When I'm tryna add new board to list of boards it's always overwrites
fun String.replaceUnusualSymbols() =
replace(", ", "").replace("[", "").replace("]", "")
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>) = indices.forEach {
println("|${board[it].contentToString().replaceUnusualSymbols()}|")
}
fun main() {
var board = Array(3) { Array(3) { ' ' } }
val game: ArrayList<Array<Array<Char>>> = arrayListOf()
board[0][0] = 'X'
game.add(board) // add first board
board[1][1] = 'X'
game.add(board) // add second board
println(game.size)
game.forEach {
it.printBoard(board) // but outputs always second board
}
}
How to fix this?
There are two problems.
Array is a mutable collection. Since you only created one var board, there's only once instance - which is mutated every time you do board[0][0] = 'X' or board[0][1] = '0'.
it.printBoard(board) isn't printing each board, it's only printing the same board.
Copying the mutable Array
You can try copying the board every time you want to add the current board state to game.
Note that because board is a nested array, just doing board.copyOf() isn't enough - this won't copy recursively. So we have to specifically copy each row of board as well.
Because map {} returns a List and board is an Array, we have to convert the copied result using toTypedArray().
board[0][0] = 'X'
game.add(board.map { row -> row.copyOf() }.toTypedArray()) // add first board
board[1][1] = '0'
game.add(board.map { row -> row.copyOf() }.toTypedArray()) // add second board
What we end up with is something that's pretty clunky and verbose. We can try making an extension function for the copy, but then we'll still have the risk involved with mutable collections. What happens if at some point you forget to copy the board?
To keep this answer short and focused, I'll leave refactoring to use immutable collections as an exercise for the reader, with some hints:
How can you use a class to encapsulate the game logic?
Instead of using a List<List<Char>>, can you create a data class?
Can you create a function that both 1. adds a new symbol to the board, and 2. saves the current game state, in one go?
printBoard()
printBoard() is an extension function has both a receiver and an argument of type Array<Array<Char>>, which is unusual.
// receiver
// ↓
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>)
// ↑
// argument
This is causing one bug. When you call it.printBoard(board), it will use the indices of it, but it will print the values of board.
// the indices come from the receiver
// ↓
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>) = indices.forEach {
println("|${board[it].contentToString().replaceUnusualSymbols()}|")
} // ↑
// the values come from the argument
So when you use it in a loop like this
game.forEach {
// argument
// ↓
it.printBoard(board)
// ↑
// receiver
}
it won't print each board.
Here's a fixed version that will only print the receiver.
fun Array<Array<Char>>.printBoard() = indices.forEach {
println("|${this[it].contentToString().replaceUnusualSymbols()}|")
}
Replace replaceUnusualSymbols() with joinToString()
And I'll go one step further - we can get rid of contentToString() and replaceUnusualSymbols() and use the Kotlin stdlib function joinToString():
fun Array<Array<Char>>.printBoard() {
val boardString = joinToString(
separator = "\n",
prefix = "\n",
) { row ->
row.joinToString(" ", prefix = "|", postfix = "|")
}
println(boardString)
}

Im working on a calculator app with some complex formulas

The Calculator code:
#IBAction func Calc(sender: UIButton) {
var a = Int(curppm.text!)
var b = Int(tarppm.text!)
var c = Int(vol.text!)
}
I'm receiving an error showing:
initialization of variable (a,b,c) was never used.
This is my first app, kind of stuck. I appreciate any assistance.
You may solve your problem by making use of the var a,b,c.
For your example,
#IBAction func Calc(sender: UIButton) {
var a = Int(curppm.text!)
var b = Int(tarppm.text!)
var c = Int(vol.text!)
a = b + c //input any logic you want here making use of a,b,c
}
Just input your logic in the line of a = b + c
But to be honest, the scope of your variable is a problem maybe, you should probably declare it outside of the function i guess. I am not familiar of swift but most of the programming language, var only live in the function and will get destroyed after function execution.