Use compose for keyboard.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2023-01-07 14:31:40 +01:00
parent 26a44dcc18
commit ae6210073d
6 changed files with 330 additions and 285 deletions

View File

@@ -5,7 +5,7 @@ import javax.inject.Inject
class Tichu @Inject constructor() { class Tichu @Inject constructor() {
fun calculateOtherScore(score: Int?): Int? { fun calculateOtherScore(score: Int): Int? {
if (score == null) { if (score == null) {
return null return null
} }
@@ -21,10 +21,10 @@ class Tichu @Inject constructor() {
return 100 - (score % 100) return 100 - (score % 100)
} }
fun isValidRound(round: Round): Boolean { fun isValidRound(scoreA: Int?, scoreB: Int?): Boolean {
if (round.scoreA == null || round.scoreB == null) { if (scoreA == null || scoreB == null) {
return false return false
} }
return (round.scoreA!!.isMultipleOf5()) && round.scoreB!!.isMultipleOf5() && (round.scoreA!! + round.scoreB!!).isMultipleOf100() return (scoreA!!.isMultipleOf5()) && scoreB!!.isMultipleOf5() && (scoreA!! + scoreB!!).isMultipleOf100()
} }
} }

View File

@@ -42,43 +42,6 @@ class CounterFragment : FragmentBase<FragmentCounterBinding>(), MenuProvider {
this, viewLifecycleOwner, Lifecycle.State.RESUMED this, viewLifecycleOwner, Lifecycle.State.RESUMED
) )
keyboardViewModel.scoreA.observe(viewLifecycleOwner) { value ->
val tichu = Tichu()
val oldValue = currentRound.scoreA
currentRound.scoreA = value
if (ignoreNextUpdate) {
ignoreNextUpdate = false
} else {
if (currentRound.scoreA?.let { oldValue?.getAbsoluteDifference(it) } != 100) {
ignoreNextUpdate = true
currentRound.scoreB = tichu.calculateOtherScore(value)
keyboardViewModel.setScoreB(currentRound.scoreB)
}
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
}
}
keyboardViewModel.scoreB.observe(viewLifecycleOwner) { value ->
val tichu = Tichu()
val oldValue = currentRound.scoreB
currentRound.scoreB = value
if (ignoreNextUpdate) {
ignoreNextUpdate = false
} else {
if (currentRound.scoreB?.let { oldValue?.getAbsoluteDifference(it) } != 100) {
ignoreNextUpdate = true
currentRound.scoreA = tichu.calculateOtherScore(value)
keyboardViewModel.setScoreA(currentRound.scoreA)
}
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
}
}
} }
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {

View File

@@ -1,17 +1,33 @@
package me.zobrist.tichucounter.ui.counter package me.zobrist.tichucounter.ui.counter
import android.content.Context import android.icu.number.FormattedNumber
import android.os.Bundle import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import androidx.compose.foundation.layout.Column
import android.widget.EditText import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.*
import androidx.compose.ui.tooling.preview.Preview
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding
import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.ui.FragmentBase import me.zobrist.tichucounter.ui.FragmentBase
import me.zobrist.tichucounter.ui.history.IHistoryFragmentViewModel
@AndroidEntryPoint @AndroidEntryPoint
class Keyboard : FragmentBase<FragmentKeyboardBinding>() { class Keyboard : FragmentBase<FragmentKeyboardBinding>() {
@@ -19,226 +35,280 @@ class Keyboard : FragmentBase<FragmentKeyboardBinding>() {
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentKeyboardBinding override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentKeyboardBinding
get() = FragmentKeyboardBinding::inflate get() = FragmentKeyboardBinding::inflate
private var unhandledNegation: Boolean = false
private val viewModel: KeyboardViewModel by activityViewModels() private val viewModel: KeyboardViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { private val requester = FocusRequester()
private var isAFocused: Boolean = false
private var isBFocused: Boolean = false
binding.inputTeamA.setRawInputType(InputType.TYPE_NULL) var enableSubmit = mutableStateOf(false)
binding.inputTeamB.setRawInputType(InputType.TYPE_NULL)
binding.inputTeamA.requestFocus()
disableSubmitButton() override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel.enableSubmitButton.observe(viewLifecycleOwner) { enabled -> snapshotFlow { viewModel.scoreA.value }
if (enabled) enableSubmitButton() else disableSubmitButton() .onEach {
enableSubmitOnValidRound(viewModel)
} }
viewModel.scoreA.observe(viewLifecycleOwner) { value -> snapshotFlow { viewModel.scoreB.value }
updateScore(binding.inputTeamA, value) .onEach {
enableSubmitOnValidRound(viewModel)
} }
viewModel.scoreB.observe(viewLifecycleOwner) { value -> return ComposeView(requireContext()).apply {
updateScore(binding.inputTeamB, value) // Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
KeyboardView(viewModel)
} }
setListeners()
} }
private fun setListeners() {
binding.inputTeamA.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
} }
} }
binding.inputTeamB.setOnFocusChangeListener { _, b -> private fun enableSubmitOnValidRound(viewModel: IKeyboardViewModel) {
if (b) { enableSubmit.value = try {
hideKeyboard() val tichu = Tichu()
} if(tichu.isValidRound(viewModel.scoreA.value.toInt(), viewModel.scoreB.value.toInt()))
} {
binding.buttonAdd100.setOnClickListener {
var value = getActiveValue()
value = if (value != null) {
value + 100
} else {
100
}
setActiveValue(value)
}
binding.buttonSub100.setOnClickListener {
var value = getActiveValue()
value = if (value != null) {
value!! - 100
} else {
-100
}
setActiveValue(value)
}
binding.button0.setOnClickListener {
appendToFocusedScore(0)
}
binding.button1.setOnClickListener {
appendToFocusedScore(1)
}
binding.button2.setOnClickListener {
appendToFocusedScore(2)
}
binding.button3.setOnClickListener {
appendToFocusedScore(3)
}
binding.button4.setOnClickListener {
appendToFocusedScore(4)
}
binding.button5.setOnClickListener {
appendToFocusedScore(5)
}
binding.button6.setOnClickListener {
appendToFocusedScore(6)
}
binding.button7.setOnClickListener {
appendToFocusedScore(7)
}
binding.button8.setOnClickListener {
appendToFocusedScore(8)
}
binding.button9.setOnClickListener {
appendToFocusedScore(9)
}
binding.buttonInv.setOnClickListener {
var value = getActiveValue()
if (value == null) {
unhandledNegation = if (getActiveText() == "-") {
setActiveText("")
false
} else {
setActiveText("-")
true true
} }
} else { false
value = value?.times(-1) } catch(_:Exception)
setActiveValue(value) {
false
} }
} }
binding.buttonBack.setOnClickListener {
var value = getActiveValue()
if (value != null) {
value = try {
value.toString().dropLast(1).toInt()
} catch (e: Exception) {
null
}
}
setActiveValue(value)
}
binding.submit.setOnClickListener {
viewModel.submitButtonClicked()
}
}
private fun hideKeyboard() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.hideSoftInputFromWindow(view?.windowToken, 0)
}
private fun giveFocusToAIfNone() { private fun giveFocusToAIfNone() {
if (!binding.inputTeamA.isFocused && !binding.inputTeamB.isFocused) { if(!isAFocused && !isBFocused)
binding.inputTeamA.requestFocus() {
requester.requestFocus()
} }
} }
private fun updateScore(field: EditText, score: Int?) { private fun appendToFocusedScore(toAppend: String, viewModel: IKeyboardViewModel) {
if (score == null) { val value = getActiveValue(viewModel)
field.setText("") value.value += toAppend
return updateOtherScore(viewModel)
} }
val text = try { private fun updateOtherScore(viewModel: IKeyboardViewModel) {
score.toString() val value = getActiveValue(viewModel)
try {
val tichu = Tichu()
val myScore = value.value.toInt()
val hisScore = tichu.calculateOtherScore(myScore)
if(tichu.isValidRound(myScore, hisScore))
{
updateInactiveValue(hisScore?.toString() ?: "", viewModel)
} else
{
updateInactiveValue("", viewModel)
}
} catch(_: Exception) {
updateInactiveValue("", viewModel)
}
}
private fun negateActiveInput(viewModel: IKeyboardViewModel) {
val value = getActiveValue(viewModel)
if(value.value.contains("-"))
{
value.value = value.value.replace("-", "")
} else {
value.value = "-" + value.value
}
updateOtherScore(viewModel)
}
private fun addToActiveInput(toAdd: Int, viewModel: IKeyboardViewModel) {
val value = getActiveValue(viewModel)
try {
val temp = value.value.toInt() + toAdd
value.value = temp.toString()
} catch (e: Exception) { } catch (e: Exception) {
"" value.value = toAdd.toString()
} }
field.setText(text)
} }
private fun appendToFocusedScore(toAppend: Int) { private fun removeFromActiveInput(viewModel: IKeyboardViewModel) {
var value = getActiveValue() val value = getActiveValue(viewModel)
if(value.value != "") {
if (value != null) { value.value = value.value.dropLast(1)
value = value.times(10)
value = value.plus(toAppend)
} else {
value = toAppend
if (unhandledNegation) {
value = value.times(-1)
unhandledNegation = false
} }
} updateOtherScore(viewModel)
setActiveValue(value)
} }
private fun getActiveValue(): Int? { private fun getActiveValue(viewModel: IKeyboardViewModel): MutableState<String> {
giveFocusToAIfNone() giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) { if (isBFocused) {
return viewModel.scoreA.value return viewModel.scoreB
} }
return viewModel.scoreB.value return viewModel.scoreA
} }
private fun setActiveValue(value: Int?) { private fun updateInactiveValue(value: String, viewModel: IKeyboardViewModel){
giveFocusToAIfNone() giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) { if (isBFocused) {
viewModel.setScoreA(value) viewModel.scoreA.value = value
} else { } else
viewModel.setScoreB(value) {
viewModel.scoreB.value = value
}
try {
val tichu = Tichu()
enableSubmit.value = tichu.isValidRound(viewModel.scoreA.value.toInt(), viewModel.scoreB.value.toInt())
} catch(_: java.lang.NumberFormatException) {
enableSubmit.value = false
} }
} }
private fun getActiveText(): String {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
return binding.inputTeamA.text.toString()
}
return binding.inputTeamB.text.toString()
}
private fun setActiveText(value: String) { @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
giveFocusToAIfNone() @Preview
if (binding.inputTeamA.isFocused) { @Composable
binding.inputTeamA.setText(value) fun KeyboardView(viewModel: IKeyboardViewModel = DefaultViewModel()) {
} else { val keyboardController = LocalSoftwareKeyboardController.current
binding.inputTeamB.setText(value)
Column {
Row {
CompositionLocalProvider(
LocalTextToolbar provides EmptyTextToolbar
) {
TextField(
value = viewModel.scoreA.value,
onValueChange = { },
placeholder = { Text("0") },
singleLine = true,
modifier = Modifier
.onFocusChanged {
keyboardController?.hide()
isAFocused = it.isFocused
}
.focusRequester(requester)
.weight(1f),
colors = TextFieldDefaults.textFieldColors(
cursorColor = Color.Transparent,
errorCursorColor = Color.Transparent
)
)
TextField(
value = viewModel.scoreB.value,
onValueChange = { },
placeholder = { Text("0") },
singleLine = true,
modifier = Modifier
.onFocusChanged {
keyboardController?.hide()
isBFocused = it.isFocused
}
.weight(1f),
colors = TextFieldDefaults.textFieldColors(
cursorColor = Color.Transparent,
errorCursorColor = Color.Transparent
)
)
} }
} }
private fun enableSubmitButton() { Row {
binding.submit.imageAlpha = 255 // 0 being transparent and 255 being opaque Button(
binding.submit.isEnabled = true onClick = { appendToFocusedScore("1", viewModel) },
modifier = Modifier.weight(1F)
) { Text("1") }
Button(
onClick = { appendToFocusedScore("2", viewModel ) },
modifier = Modifier.weight(1F)
) { Text("2") }
Button(
onClick = { appendToFocusedScore("3", viewModel) },
modifier = Modifier.weight(1F)
) { Text("3") }
Button(onClick = { addToActiveInput(100, viewModel) }, modifier = Modifier.weight(1F)) { Text("+100") }
}
Row {
Button(
onClick = { appendToFocusedScore("4", viewModel) },
modifier = Modifier.weight(1F)
) { Text("4") }
Button(
onClick = { appendToFocusedScore("5", viewModel) },
modifier = Modifier.weight(1F)
) { Text("5") }
Button(
onClick = { appendToFocusedScore("6", viewModel) },
modifier = Modifier.weight(1F)
) { Text("6") }
Button(onClick = { addToActiveInput(-100, viewModel) }, modifier = Modifier.weight(1F)) { Text("-100") }
}
Row {
Button(
onClick = { appendToFocusedScore("7", viewModel) },
modifier = Modifier.weight(1F)
) { Text("7") }
Button(
onClick = { appendToFocusedScore("8", viewModel) },
modifier = Modifier.weight(1F)
) { Text("8") }
Button(
onClick = { appendToFocusedScore("9", viewModel) },
modifier = Modifier.weight(1F)
) { Text("9") }
Button(onClick = { removeFromActiveInput(viewModel) }, modifier = Modifier.weight(1F)) { Text("DEL") }
}
Row {
Button(onClick = { negateActiveInput(viewModel) }, modifier = Modifier.weight(1F)) { Text("+/-") }
Button(
onClick = { appendToFocusedScore("0", viewModel) },
modifier = Modifier.weight(1F)
) { Text("0") }
Spacer(modifier = Modifier.weight(1F))
Button(onClick = { submit(viewModel) }, modifier = Modifier.weight(1F), enabled = enableSubmit.value) { Text("ENTER") }
}
}
} }
private fun disableSubmitButton() { private fun submit(viewModel: IKeyboardViewModel) {
binding.submit.imageAlpha = 60 // 0 being transparent and 255 being opaque viewModel.submitScore(viewModel.scoreA.value.toInt(), viewModel.scoreB.value.toInt())
binding.submit.isEnabled = false viewModel.scoreA.value = ""
viewModel.scoreB.value = ""
enableSubmit.value = false
} }
object EmptyTextToolbar : TextToolbar {
override val status: TextToolbarStatus = TextToolbarStatus.Hidden
override fun hide() {}
override fun showMenu(
rect: Rect,
onCopyRequested: (() -> Unit)?,
onPasteRequested: (() -> Unit)?,
onCutRequested: (() -> Unit)?,
onSelectAllRequested: (() -> Unit)?,
) {
}
}
internal class DefaultViewModel : IKeyboardViewModel {
override var scoreA: MutableState<String> = mutableStateOf("")
override var scoreB: MutableState<String> = mutableStateOf("")
override fun submitScore(scoreA: Int, scoreB: Int) {
}
}
} }

View File

@@ -1,54 +1,37 @@
package me.zobrist.tichucounter.ui.counter package me.zobrist.tichucounter.ui.counter
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.repository.GameRepository import me.zobrist.tichucounter.repository.GameRepository
import javax.inject.Inject import javax.inject.Inject
interface IKeyboardViewModel {
var scoreA: MutableState<String>
var scoreB: MutableState<String>
fun submitScore(scoreA: Int, scoreB: Int)
}
@HiltViewModel @HiltViewModel
class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepository) : class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepository) :
ViewModel() { ViewModel(), IKeyboardViewModel {
private val _scoreA: MutableLiveData<Int?> = MutableLiveData() override var scoreA = mutableStateOf( "")
private val _scoreB: MutableLiveData<Int?> = MutableLiveData() override var scoreB = mutableStateOf("")
private val _enableSubmitButton: MutableLiveData<Boolean> = MutableLiveData()
val scoreA: LiveData<Int?>
get() {
return _scoreA
}
val scoreB: LiveData<Int?> override fun submitScore(scoreA: Int, scoreB: Int) {
get() {
return _scoreB
}
val enableSubmitButton: LiveData<Boolean>
get() {
return _enableSubmitButton
}
fun setScoreA(score: Int?) {
_scoreA.value = score
}
fun setScoreB(score: Int?) {
_scoreB.value = score
}
fun setSubmitButtonEnable(enabled: Boolean) {
_enableSubmitButton.value = enabled
}
fun submitButtonClicked() {
viewModelScope.launch { viewModelScope.launch {
gameRepository.addRoundToActiveGame(scoreA.value!!, scoreB.value!!) gameRepository.addRoundToActiveGame(scoreA, scoreB)
_scoreA.value = null
_scoreB.value = null
setSubmitButtonEnable(false)
} }
} }
} }

View File

@@ -5,11 +5,13 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -82,23 +84,28 @@ class HistoryFragment : Fragment() {
Row() { Row() {
Text( Text(
text = game.nameA, text = game.nameA,
style = MaterialTheme.typography.headlineSmall) style = MaterialTheme.typography.headlineSmall
)
Text( Text(
text = game.scoreA.toString(), text = game.scoreA.toString(),
style = MaterialTheme.typography.headlineSmall) style = MaterialTheme.typography.headlineSmall
)
} }
Row() { Row() {
Text( Text(
text = game.nameB, text = game.nameB,
style = MaterialTheme.typography.headlineSmall) style = MaterialTheme.typography.headlineSmall
)
Text( Text(
text = game.scoreB.toString(), text = game.scoreB.toString(),
style = MaterialTheme.typography.headlineSmall) style = MaterialTheme.typography.headlineSmall
)
} }
Row() { Row() {
Text( Text(
text = format.format(game.modified), text = format.format(game.modified),
style = MaterialTheme.typography.labelSmall) style = MaterialTheme.typography.labelSmall
)
} }
} }
} }
@@ -109,8 +116,30 @@ class HistoryFragment : Fragment() {
override val gameAndHistory: State<List<GameAndScore>> override val gameAndHistory: State<List<GameAndScore>>
get() { get() {
val tempData = mutableListOf<GameAndScore>() val tempData = mutableListOf<GameAndScore>()
tempData.add(GameAndScore(false, "TeamA1sdfdsf", "TeamB1", Date(), Date(), 1, 10, 50)) tempData.add(
tempData.add(GameAndScore(true, "TeamA2", "TeamB2sdfsdf", Date(), Date(), 2, 20, 60)) GameAndScore(
false,
"TeamA1sdfdsf",
"TeamB1",
Date(),
Date(),
1,
10,
50
)
)
tempData.add(
GameAndScore(
true,
"TeamA2",
"TeamB2sdfsdf",
Date(),
Date(),
2,
20,
60
)
)
tempData.add(GameAndScore(false, "TeamA3", "TeamB3", Date(), Date(), 3, 30, 70)) tempData.add(GameAndScore(false, "TeamA3", "TeamB3", Date(), Date(), 3, 30, 70))
tempData.add(GameAndScore(false, "TeamA4", "TeamB4", Date(), Date(), 4, 40, 80)) tempData.add(GameAndScore(false, "TeamA4", "TeamB4", Date(), Date(), 4, 40, 80))
tempData.add(GameAndScore(false, "TeamA5", "TeamB5", Date(), Date(), 5, 50, 90)) tempData.add(GameAndScore(false, "TeamA5", "TeamB5", Date(), Date(), 5, 50, 90))

View File

@@ -37,26 +37,26 @@ class TichuUnitTest {
assertGeneratedRound(tichu, 400, 0) assertGeneratedRound(tichu, 400, 0)
//Good rounds trough Tichu //Good rounds trough Tichu
assertValidRound(tichu, Round(1, 0, 0)) assertValidRound(tichu, 0, 0)
assertValidRound(tichu, Round(1, -100, 0)) assertValidRound(tichu, -100, 0)
//Bad rounds //Bad rounds
assertInvalidRound(tichu, Round(1, 5, 12)) assertInvalidRound(tichu, 5, 12)
assertInvalidRound(tichu, Round(1, 12, 5)) assertInvalidRound(tichu, 12, 5)
assertInvalidRound(tichu, Round(1, 5, 55)) assertInvalidRound(tichu, 5, 55)
} }
private fun assertGeneratedRound(tichu: Tichu, scoreA: Int, expectedScoreB: Int) { private fun assertGeneratedRound(tichu: Tichu, scoreA: Int, expectedScoreB: Int) {
val round = Round(1, scoreA, tichu.calculateOtherScore(scoreA)) val scoreB = tichu.calculateOtherScore(scoreA)
assertEquals(expectedScoreB, round.scoreB) assertEquals(expectedScoreB, scoreB)
assertTrue(tichu.isValidRound(round)) assertTrue(tichu.isValidRound(scoreA, scoreB!!))
} }
private fun assertInvalidRound(tichu: Tichu, round: Round) { private fun assertInvalidRound(tichu: Tichu, scoreA: Int, scoreB: Int) {
assertFalse(tichu.isValidRound(round)) assertFalse(tichu.isValidRound(scoreA, scoreB))
} }
private fun assertValidRound(tichu: Tichu, round: Round) { private fun assertValidRound(tichu: Tichu, scoreA: Int, scoreB: Int) {
assertTrue(tichu.isValidRound(round)) assertTrue(tichu.isValidRound(scoreA, scoreB))
} }
} }