Day69 of #100DaysOfCode

#CodeTogether Day 69/100

Hii folks 🙌

Today I will be continuing the same pathway in which we’ll Update game restart logic and will complete the app.

Unit 3: Navigation

Pathway 2: Architecture componentsSource

https://developer.android.com/courses/android-basics-kotlin/course

  • We will run the app, and play the game through all the words. In the Congratulations! alert dialog, when we click PLAY AGAIN. The app won’t let us play again because the word count has now reached the value MAX_NO_OF_WORDS. We need to reset the word count to 0 to play the game again from the beginning.
  • To reset the app data, in GameViewModel we will add a method called reinitializeData(). It will set the score and word count to 0 andClear the word list and call getNextWord() method.
/*
* Re-initializes the game data to restart the game.
*/

fun reinitializeData() {
_score = 0
_currentWordCount = 0
wordsList.clear()
getNextWord()
}
  • In GameFragment at the top of the method restartGame(), we make a call to the newly created method, reinitializeData().
private fun restartGame() {
viewModel.reinitializeData()
setErrorTextField(false)
updateNextWordOnScreen()
}

Now we click on Play Again, we should be able to successfully play the game again!

Final Code

GameFragment.kt

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.example.android.unscramble.R
import com.example.android.unscramble.databinding.GameFragmentBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder

/**
* Fragment where the game is played, contains the game logic.
*/

class GameFragment : Fragment() {

private val viewModel: GameViewModel by viewModels()

// Binding object instance with access to the views in the game_fragment.xml layout
private lateinit var binding: GameFragmentBinding

// Create a ViewModel the first time the fragment is created.
// If the fragment is re-created, it receives the same GameViewModel instance created by the
// first fragment

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout XML file and return a binding object instance
binding = GameFragmentBinding.inflate(inflater, container, false)
Log.d("GameFragment", "GameFragment created/re-created!")
Log.d("GameFragment", "Word: ${viewModel.currentScrambledWord} " +
"Score: ${viewModel.score} WordCount: ${viewModel.currentWordCount}")
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Setup a click listener for the Submit and Skip buttons.
binding.submit.setOnClickListener { onSubmitWord() }
binding.skip.setOnClickListener { onSkipWord() }
// Update the UI
updateNextWordOnScreen()
binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(
R.string.word_count, 0, MAX_NO_OF_WORDS)
}

/*
* Checks the user's word, and updates the score accordingly.
* Displays the next scrambled word.
* After the last word, the user is shown a Dialog with the final score.
*/

private fun onSubmitWord() {
val playerWord = binding.textInputEditText.text.toString()

if (viewModel.isUserWordCorrect(playerWord)) {
setErrorTextField(false)
if (viewModel.nextWord()) {
updateNextWordOnScreen()
} else {
showFinalScoreDialog()
}
} else {
setErrorTextField(true)
}
}

/*
* Skips the current word without changing the score.
*/

private fun onSkipWord() {
if (viewModel.nextWord()) {
setErrorTextField(false)
updateNextWordOnScreen()
} else {
showFinalScoreDialog()
}
}

/*
* Gets a random word for the list of words and shuffles the letters in it.
*/

private fun getNextScrambledWord(): String {
val tempWord = allWordsList.random().toCharArray()
tempWord.shuffle()
return String(tempWord)
}

/*
* Creates and shows an AlertDialog with the final score.
*/

private fun showFinalScoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.congratulations))
.setMessage(getString(R.string.you_scored, viewModel.score))
.setCancelable(false)
.setNegativeButton(getString(R.string.exit)) { _, _ ->
exitGame()
}
.setPositiveButton(getString(R.string.play_again)) { _, _ ->
restartGame()
}
.show()
}

/*
* Re-initializes the data in the ViewModel and updates the views with the new data, to
* restart the game.
*/

private fun restartGame() {
viewModel.reinitializeData()
setErrorTextField(false)
updateNextWordOnScreen()
}

/*
* Exits the game.
*/

private fun exitGame() {
activity?.finish()
}

override fun onDetach() {
super.onDetach()
Log.d("GameFragment", "GameFragment destroyed!")
}

/*
* Sets and resets the text field error status.
*/

private fun setErrorTextField(error: Boolean) {
if (error) {
binding.textField.isErrorEnabled = true
binding.textField.error = getString(R.string.try_again)
} else {
binding.textField.isErrorEnabled = false
binding.textInputEditText.text = null
}
}

/*
* Displays the next scrambled word on screen.
*/

private fun updateNextWordOnScreen() {
binding.textViewUnscrambledWord.text = viewModel.currentScrambledWord
}
}

GameViewModel.kt

import android.util.Log
import androidx.lifecycle.ViewModel

/**
* ViewModel containing the app data and methods to process the data
*/

class GameViewModel : ViewModel(){
private var _score = 0
val score: Int
get() = _score

private var _currentWordCount = 0
val currentWordCount: Int
get() = _currentWordCount

private lateinit var _currentScrambledWord: String
val currentScrambledWord: String
get() = _currentScrambledWord

// List of words used in the game
private var wordsList: MutableList<String> = mutableListOf()
private lateinit var currentWord: String

init {
Log.d("GameFragment", "GameViewModel created!")
getNextWord()
}

override fun onCleared() {
super.onCleared()
Log.d("GameFragment", "GameViewModel destroyed!")
}

/*
* Updates currentWord and currentScrambledWord with the next word.
*
/
private fun getNextWord() {
currentWord = allWordsList.random()
val tempWord = currentWord.toCharArray()
tempWord.shuffle()

while (String(tempWord).equals(currentWord, false)) {
tempWord.shuffle()
}
if (wordsList.contains(currentWord)) {
getNextWord()
} else {
_currentScrambledWord = String(tempWord)
++_currentWordCount
wordsList.add(currentWord)
}
}

/*
* Re-initializes the game data to restart the game.
*/

fun reinitializeData() {
_score = 0
_currentWordCount = 0
wordsList.clear()
getNextWord()
}


/*
* Increases the game score if the player's word is correct.
*/

private fun increaseScore() {
_score += SCORE_INCREASE
}

/*
* Returns true if the player word is correct.
* Increases the score accordingly.
*/

fun isUserWordCorrect(playerWord: String): Boolean {
if (playerWord.equals(currentWord, true)) {
increaseScore()
return true
}
return false
}

/*
* Returns true if the current word count is less than MAX_NO_OF_WORDS
*/

fun nextWord(): Boolean {
return if (_currentWordCount < MAX_NO_OF_WORDS) {
getNextWord()
true
} else false
}
}

That is all for Day69 ✅

Thanks for reading, See you tomorrow!

If you are reading my #100Days Journey, feel free to drop by ;)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store