A simple scramble game - that was my original intention. But I needed that words to be in Kannada.
- Get a word list - can be inside the code
- Scramble the list to get random words
- Select a random word
- Separate the letters of the word
- Now scramble the letters of the word
- Display the scrambled letters
- Let the user click on each letter in order to unscramble it
Simple. Quite simple. Only difficulty in this case - our dear mother tongue which uses two unicode characters to display a compound letter like kaa, and uses more to display a ottakshara like kka kku etc.
So if the letter is any of the vowel sounds or half letter, we need to add this the the existing a character in step 4. So basically the the list size will be lesser than the string length.
e.g.
ಶಾಲೆಯಲ್ಲಿ must give us only four letters - ಶಾ, ಲೆ, ಯ and ಲ್ಲಿ
So here is the code in Kotlin
There are two kotlin files - one is viewmodel and the other is activity
package com.hegdeapps.scramble import android.graphics.Color import android.os.CountDownTimer import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import java.util.* import kotlin.random.Random class ScrViewModel: ViewModel() { var mTimeRem: MutableLiveData<Int> private var mOttakshara: Boolean = false var mCurrentWord: MutableLiveData<String> lateinit var timer:MutableLiveData<CountDownTimer> private val MyStringArray = arrayOf("ಅಡಗಿಸಿದೆನು","ಅಪಹರಿಸು","ಕಳುಹಿಸಬಹುದು","ಕಾಪಾಡಲಿ","ಕೂಗಿಕೊಂಡನು", "ಕೊರತೆಯಿಂದ","ಗಾಳಿಯಿಂದ","ಗೋಡೆಗಳಿಗೆ","ಚಿಂತಿಸಬೇಡ","ಜೊತೆಯಾಗಿ", "ಲಸಿಕೆ","ವಸ್ತುನಿಷ್ಠ","ಮಾವಿನತೋಪು","ಕಾರಭಾರಗಳು","ಕರ್ತವ್ಯನಿಷ್ಠ ","ಕಿರಾತಕ","ಕಿರುಕಾಣಿಕೆ","ಕುರುಹುಗಳನ್ನು", "ಕೂಗಾಟದಿಂದ","ಕಲ್ಮಶವಲ್ಲದ","ಕ್ರಮಬದ್ಧವಾಗಿರುವ","ಅಕ್ಕರೆಯಿಂದ","ಸಕ್ಕರೆಯಿಲ್ಲದ","ಕೊಕ್ಕರೆಯ","ಶುಕ್ರವಾರ", "ಚೊಕ್ಕವಾದ","ಬಾಗಿಲಿನೊಳಗೆ","ಬಗ್ಗುಬಡೆದನು","ಸಿಗ್ಗಿಲ್ಲದೆ","ಗಂಟಿಲನೊಳಗೆ","ಸುಂಟರಗಾಳಿ","ನೆಂಟರಿಗಾಗಿತ್ತು", "ಮಲ್ಲಿಗೆಯ","ಮಿಂಚಿನ","ಸಂಚಿನಿಂದ","ಕೊಂಚವಾದರೂ","ಲೇಖನಿಯ","ಪುಸ್ತಕದೊಳಗೆ","ಪವಿತ್ರವಾದ","ಅರ್ಪಿಸಿದರು", "ಸುಪ್ರತೀಕ","ಕರ್ಪೂರದ","ಬೆಳಗಿನ","ಮುಂಜಾವು","ಸಂಜೆಯಲ್ಲಿ","ರಸ್ತೆಯಲ್ಲಿ","ಬೆಳಕಿಲ್ಲದೆ","ಬೇಕಾಗಿಲ್ಲ","ಸಂತೆಯಲ್ಲಿ", "ಮಂದಿರದೊಳಗೆ","ಸುಂದರವಲ್ಲದ","ಕಂದರದೊಳಗಿಂದ", "ಮಂದಾರಕುಸುಮ","ಸರ್ಪಶಯನ","ವಿಪ್ರೋತ್ತಮ","ವಿಳಂಬವಾಯಿತು","ವೇಳೆಯಿಲ್ಲ","ಸಮಯವ್ಯರ್ಥ","ಅಸಮಾಧಾನ") init { mCurrentWord = MutableLiveData() mCurrentWord.value = "" mTimeRem = MutableLiveData() timer = MutableLiveData() startTimer() } private var mUnUsedWordList: ArrayList<String> = MyStringArray.toList() as ArrayList<String> /** * create a display a puzzle */ fun createPuzzle() { val rnd: Int = Random(Calendar.getInstance().timeInMillis).nextInt(mUnUsedWordList.size) val word: String = mUnUsedWordList[rnd] mCurrentWord.value = word mUnUsedWordList.remove(word) } /** * Split the word into characters * Note that vowel form like aakar etc will be another unicode. So ki infact will be two characters k+i. * That is taken care - codes for vowel parts are 0x0cbe to 0x0ccd * and that of am is 0x0c82 and aha is 0x0c83 */ fun getKannadaCharArray(word: String): List<String> { var chList = mutableListOf<String>() var prevString = "" var i = 0 for(ch in word){ var str:String if(mOttakshara){ str = prevString.plus(ch) chList.set(i-1,str) mOttakshara =false; }else { if (ch.toInt() == 0x0ccd) { mOttakshara = true str = prevString.plus(ch) chList.set(i-1, str) } else if (ch.toInt() == 0x0c82 || ch.toInt() == 0x0c83 || (ch.toInt() >= 0x0cbe && ch.toInt() <= 0x0ccc) ) { str = prevString.plus(ch) chList.set(i-1, str) } else { str = ch.toString() chList.add(i, str) i++ } } prevString = str } return chList as List<String> } fun startTimer() { val oneMinute:Long = 60*1000 timer.value= object: CountDownTimer(oneMinute,1000){ override fun onFinish() { mTimeRem?.value = -1 } override fun onTick(millisUntilFinished: Long) { updateTime(millisUntilFinished) } } timer.value?.start() } private fun updateTime(m:Long) { val timeRem = (m )/1000.0 mTimeRem?.value = timeRem.toInt() } override fun onCleared() { super.onCleared() timer.value?.cancel() } fun restartGame() { timer.value?.cancel() mUnUsedWordList.clear() mUnUsedWordList = MyStringArray.toList() as ArrayList<String> createPuzzle() startTimer() } }
package com.hegdeapps.scramble import android.content.Context import android.content.DialogInterface import android.graphics.Color import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.CountDownTimer import android.util.Log import android.view.Gravity import android.view.View import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.view.children import androidx.databinding.DataBindingUtil import androidx.fragment.app.DialogFragment import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders import com.hegdeapps.scramble.databinding.ActivityMainBinding import org.w3c.dom.Text import kotlin.random.Random //TODO how to pause timer when game not visible?? class MainActivity : AppCompatActivity(), View.OnClickListener { private lateinit var timeObserver: Observer<Int> // private lateinit var timer:CountDownTimer private val MAX_CHILDREN: Int=7 private lateinit var viewModel: ScrViewModel private var mScore: Int = 0 private var mOttakshara: Boolean=false private lateinit var dataBinding: ActivityMainBinding private var mInputIndex: Int=0 // private var mCurrentWord: String="" private var mLetterIndex: Int=0 private lateinit var mTimeRem: LiveData<Long> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) dataBinding = DataBindingUtil.setContentView(this,R.layout.activity_main) // dataBinding.setLifecycleOwner (this) viewModel = ViewModelProviders.of(this).get(ScrViewModel::class.java) val wordObserver = Observer<String> { newWord -> displayPuzzle(viewModel.getKannadaCharArray(newWord)) } viewModel.mCurrentWord.observe(this,wordObserver) timeObserver = Observer<Int>{ newTime->if(newTime!=-1) dataBinding.timeLeft.setText(newTime.toString()) else { viewModel.mTimeRem?.removeObserver(timeObserver) showQuizEndDialog() } } viewModel.mTimeRem?.observe(this,timeObserver) //viewModel.mTimeRem?.value = 100 /* viewModel.mTimeRem?.observe(this, Observer{ time->if(time<=0) showQuizEndDialog() else dataBinding.timeLeft.text = time.toString() }) */ // viewModel.startTimer() viewModel.createPuzzle() // displayPuzzle() dataBinding.done.setOnClickListener(this) } /** * create a display a puzzle */ private fun displayPuzzle( oldList:List<String>) { // startTimer() /* val rnd:Int= Random(Calendar.getInstance().timeInMillis).nextInt(mUnUsedWordList.size) val word: String = mUnUsedWordList[rnd] mCurrentWord = word mUnUsedWordList.remove(word) var chList:List<String> = getKannadaCharArray(word) chList = chList.shuffled()*/ // val chList = viewModel.createPuzzle() val chList = oldList.shuffled() var tvArray= Array<TextView?>(chList.size){ null} val letterLayout = dataBinding.inputletters for(i in 0..chList.size-1) { tvArray[i] = letterLayout.getChildAt(i) as TextView? tvArray[i]?.text = chList.get(i) tvArray[i]?.setOnClickListener { view -> val str = (view as TextView).text putStringInLetterBox(str,view ) } } for(i in chList.size..MAX_CHILDREN) { letterLayout.getChildAt(i).visibility = View.GONE } letterLayout.refreshDrawableState() val tvArrayInput : Array<TextView?> = Array (chList.size){null} val letterBoxLayout = dataBinding.letterbox for(i in 0..chList.size-1) { tvArrayInput[i] = letterBoxLayout.getChildAt(i) as TextView? tvArrayInput[i]?.text = "" tvArrayInput[i]?.setOnClickListener { v -> val str = (v as TextView).text putStringInInputBox(str,v) } } for(i in chList.size..MAX_CHILDREN) { letterBoxLayout.getChildAt(i)?.visibility = View.GONE } letterBoxLayout.refreshDrawableState() } private fun showQuizEndDialog() { var bldr : AlertDialog.Builder = AlertDialog.Builder(this) bldr.setMessage("ನಿಮ್ಮ ಅಂಕೆಗಳು $mScore"); bldr.setPositiveButton("ನಿಲ್ಲಿಸಿ", { dialogInterface: DialogInterface, i: Int -> this.finish() }) bldr.setNegativeButton("ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ ", { dialogInterface: DialogInterface, i: Int -> this.restartGame() }) bldr.show() } private fun restartGame() { viewModel?.mTimeRem.value = 1000 mScore = 0 clearPuzzle() viewModel.mTimeRem?.observe(this,timeObserver) viewModel.restartGame() } override fun onClick(v: View?) { if (v?.id == R.id.done) { checkAnswer() clearPuzzle() viewModel.createPuzzle() // displayPuzzle() return } //Button does not have a tag. Others have val tag: String = v?.getTag() as String if (tag.equals("input")) { val str = (v as TextView).text putStringInLetterBox(str, v) } else if (tag.equals("letterbox")) { val str = (v as TextView).text putStringInInputBox(str, v) } } private fun checkAnswer() { val layout:LinearLayout = dataBinding.letterbox val c = layout.getChildCount() var word="" for(i in 0..c-1){ val tv = layout.getChildAt(i) val str = (tv as TextView).text.toString() word = word+str; } if(word.equals(viewModel.mCurrentWord.value)){ Toast.makeText(this,"Correct",Toast.LENGTH_SHORT).show() // showMessage("Correct") mScore++ }else{ Toast.makeText(this,"wrong",Toast.LENGTH_SHORT).show() //showMessage("Wrong") } // createPuzzle() } /*private fun showMessage(s: String) { val b:AlertDialog.Builder = AlertDialog.Builder(this) b.setMessage(s) b.setOnDismissListener { this.createPuzzle() } b.show() *//* val t = Toast.makeText(this,s,Toast.LENGTH_SHORT) t.show() this.createPuzzle()*//* }*/ private fun clearPuzzle() { val inputLayout = dataBinding.inputletters for(tv in inputLayout.children) { (tv as TextView).text = "" tv.visibility = View.VISIBLE } val letterBoxLayout = dataBinding.letterbox for(tv in letterBoxLayout.children) { (tv as TextView).text = "" tv.visibility = View.VISIBLE } mLetterIndex = 0 } private fun putStringInLetterBox(str: CharSequence?,v:View) { val letterboxlayout = dataBinding.letterbox val emptyIndex = findEmptyBox(letterboxlayout) val childTv:TextView = letterboxlayout.getChildAt(emptyIndex) as TextView childTv.text = str childTv.visibility = View.VISIBLE v.visibility= View.INVISIBLE } private fun putStringInInputBox(str: CharSequence?,v:View) { val inputboxlayout = dataBinding.inputletters val emptyIndex = findEmptyBox(inputboxlayout) val childTv:TextView = inputboxlayout.getChildAt(emptyIndex) as TextView childTv.text = str childTv.visibility = View.VISIBLE v.visibility= View.INVISIBLE mLetterIndex--; } private fun findEmptyBox(layout: LinearLayout?): Int { val m:Int= if(layout!=null) layout.childCount else 0 for(i in 0..m){ val view = layout?.getChildAt(i) if((view as TextView?)?.text=="" || view?.visibility == View.INVISIBLE){ return i } } return 0 } /*override fun onStop() { super.onStop() if(timer!=null) timer.cancel() }*/ }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" > <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:id="@+id/timeLeft" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="28dp" android:layout_marginEnd="32dp" android:layout_marginBottom="16dp" android:text="" android:textStyle="bold" android:fontFamily="sans-serif-medium" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/white" app:layout_constraintBottom_toTopOf="@+id/letterbox" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:text="ಶಬ್ದವನ್ನು ಸರಿಪಡಿಸಿ" android:textColor="@android:color/white" android:textStyle="bold" android:textAppearance="?android:attr/textAppearanceLarge" app:layout_constraintBottom_toTopOf="@id/timeLeft" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:id="@+id/letterbox" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginBottom="16dp" android:background="@android:color/white" android:elevation="5dp" android:gravity="center" android:orientation="horizontal" android:padding="10dp" app:layout_constraintBottom_toTopOf="@+id/inputletters" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/timeLeft"> <TextView android:id="@+id/lettertext1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/lettertext8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#f5f5f5" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> </LinearLayout> <LinearLayout android:id="@+id/inputletters" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginBottom="32dp" android:background="#83FFFFFF" android:elevation="5dp" android:gravity="center" android:orientation="horizontal" android:padding="10dp" app:layout_constraintBottom_toTopOf="@+id/done" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/letterbox"> <TextView android:id="@+id/inputtext1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> <TextView android:id="@+id/inputtext8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="2dp" android:background="#33ffffff" android:gravity="center" android:minWidth="30dp" android:minHeight="48dp" android:text="" android:textSize="24sp" /> </LinearLayout> <Button android:id="@+id/done" style="@style/Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="32dp" android:layout_marginEnd="24dp" android:layout_marginBottom="32dp" android:text="Done" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/inputletters" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
You can modify the code.
I am using live date to store time and score and word. So roation of the screen does not stop the app and recreate it
But currently thetimer does not pause when the sceen is not visible. I still have to tweak that.
The app uses viewmodel and livedata from jetpack library
Comments
Post a Comment