Game with noughts and corsses that has different ways to win:
- 4 in row horizontal
- 4 in row vertical
- 2x2 box
- Main diagonal
- Reverse diagonal
- Koin for dependency injection.
- Junit 5 & Mockito for testing.
- ViewModel, dataBinding and LiveData.
The different modules have different responsibilities:
app
-> Has the main logic of the app.core
-> Contain all the logic that could potentially use all over the app. Here I have implemented the base classes and all the extensions.components
-> Contain all the custom views that would be used all over the app.
The presentation layer is now based on three different files:
ViewModel
, Fragment
and State
Where the ViewModel would do all the logic and the states would be managed in the Fragment. For example:
The viewModel
(Does an API call) -> the viewModel
receives data from the API -> the viewModel
updates the state -> The state
is observed in the Fragment
->
when the state
is updated, then in the Fragment
we are able to make the pertinent updates.
In this project I have used two different ways to bind data to the custom view GameBoard
.
- Using 2 way dataBinding
For this, I have created a BindingAdapter and an InverseBindingAdapter for the attribute playerTurn
, where it is going to emit data to the view when user touch any cell of the board and there is no winner combination or draw.
@JvmStatic
@InverseBindingAdapter(attribute = "playerTurn", event = "playerTurnAttrChanged")
fun getPlayerTurn(view: GameBoard): Int {
return view.viewModel.player
}
@SuppressLint("ClickableViewAccessibility")
@JvmStatic
@BindingAdapter(value = ["playerTurnAttrChanged"])
fun setListener(view: GameBoard, listener: InverseBindingListener?) {
listener?.let {
view.setOnTouchListener { _, motionEvent ->
listener.onChange()
view.onTouchEvent(motionEvent)
}
}
}
- Access to variables of the view using View binding
In the GameBoard
custom view, I have created two different variables called isWinnerMovement
and isDraw
that has a custom get function that is triggered when any of them are consulted.
viewModel.checkGameStatus(
binding.gbGame.isWinnerMovement,
binding.gbGame.isDraw,
playerTurn
)
For this project, I have created unit tests for the two viewModels that I have created in this project, TTTViewModel
and GameBoardViewModel
. For these uni tests I have used
JUnit 5 and I have also created a custom annotation to be able to test LiveData objects (InstantExecutorExtension
).
BASE.GRADLE
contain all the common logic for all the different modules (app, core and components).DEPENDENCIES.GRADLE
contain a list of LinkedHashMaps with all the dependencies and all the correspondent versions.- The rest of the gradle files only contain the specific setup that each module require.