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 ->
updateScore(binding.inputTeamA, value)
}
viewModel.scoreB.observe(viewLifecycleOwner) { value ->
updateScore(binding.inputTeamB, value)
}
setListeners()
}
private fun setListeners() {
binding.inputTeamA.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
} }
}
binding.inputTeamB.setOnFocusChangeListener { _, b -> snapshotFlow { viewModel.scoreB.value }
if (b) { .onEach {
hideKeyboard() enableSubmitOnValidRound(viewModel)
} }
}
binding.buttonAdd100.setOnClickListener { return ComposeView(requireContext()).apply {
var value = getActiveValue() // Dispose of the Composition when the view's LifecycleOwner
// is destroyed
value = if (value != null) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
value + 100 setContent {
} else { MaterialTheme {
100 KeyboardView(viewModel)
}
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
}
} else {
value = value?.times(-1)
setActiveValue(value)
}
}
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() { private fun enableSubmitOnValidRound(viewModel: IKeyboardViewModel) {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? enableSubmit.value = try {
imm?.hideSoftInputFromWindow(view?.windowToken, 0) val tichu = Tichu()
if(tichu.isValidRound(viewModel.scoreA.value.toInt(), viewModel.scoreB.value.toInt()))
{
true
}
false
} catch(_:Exception)
{
false
}
} }
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 {
score.toString()
} catch (e: Exception) {
""
}
field.setText(text)
} }
private fun appendToFocusedScore(toAppend: Int) { private fun updateOtherScore(viewModel: IKeyboardViewModel) {
var value = getActiveValue() val value = getActiveValue(viewModel)
if (value != null) { try {
value = value.times(10) val tichu = Tichu()
value = value.plus(toAppend) 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 { } else {
value = toAppend value.value = "-" + value.value
if (unhandledNegation) { }
value = value.times(-1)
unhandledNegation = false 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) {
value.value = toAdd.toString()
}
}
private fun removeFromActiveInput(viewModel: IKeyboardViewModel) {
val value = getActiveValue(viewModel)
if(value.value != "") {
value.value = value.value.dropLast(1)
}
updateOtherScore(viewModel)
}
private fun getActiveValue(viewModel: IKeyboardViewModel): MutableState<String> {
giveFocusToAIfNone()
if (isBFocused) {
return viewModel.scoreB
}
return viewModel.scoreA
}
private fun updateInactiveValue(value: String, viewModel: IKeyboardViewModel){
giveFocusToAIfNone()
if (isBFocused) {
viewModel.scoreA.value = value
} else
{
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
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@Preview
@Composable
fun KeyboardView(viewModel: IKeyboardViewModel = DefaultViewModel()) {
val keyboardController = LocalSoftwareKeyboardController.current
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
)
)
}
}
Row {
Button(
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") }
} }
} }
setActiveValue(value)
} }
private fun getActiveValue(): Int? { private fun submit(viewModel: IKeyboardViewModel) {
giveFocusToAIfNone() viewModel.submitScore(viewModel.scoreA.value.toInt(), viewModel.scoreB.value.toInt())
if (binding.inputTeamA.isFocused) { viewModel.scoreA.value = ""
return viewModel.scoreA.value viewModel.scoreB.value = ""
} enableSubmit.value = false
return viewModel.scoreB.value
} }
private fun setActiveValue(value: Int?) { object EmptyTextToolbar : TextToolbar {
giveFocusToAIfNone() override val status: TextToolbarStatus = TextToolbarStatus.Hidden
if (binding.inputTeamA.isFocused) {
viewModel.setScoreA(value) override fun hide() {}
} else {
viewModel.setScoreB(value) override fun showMenu(
rect: Rect,
onCopyRequested: (() -> Unit)?,
onPasteRequested: (() -> Unit)?,
onCutRequested: (() -> Unit)?,
onSelectAllRequested: (() -> Unit)?,
) {
} }
} }
private fun getActiveText(): String { internal class DefaultViewModel : IKeyboardViewModel {
giveFocusToAIfNone() override var scoreA: MutableState<String> = mutableStateOf("")
if (binding.inputTeamA.isFocused) { override var scoreB: MutableState<String> = mutableStateOf("")
return binding.inputTeamA.text.toString()
}
return binding.inputTeamB.text.toString()
}
private fun setActiveText(value: String) { override fun submitScore(scoreA: Int, scoreB: Int) {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
binding.inputTeamA.setText(value)
} else {
binding.inputTeamB.setText(value)
} }
} }
private fun enableSubmitButton() {
binding.submit.imageAlpha = 255 // 0 being transparent and 255 being opaque
binding.submit.isEnabled = true
}
private fun disableSubmitButton() {
binding.submit.imageAlpha = 60 // 0 being transparent and 255 being opaque
binding.submit.isEnabled = false
}
} }

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))
} }
} }