Skip to main content

source code of simple memory game - Simon


Simon game is an electric memory game.  The device creates a series of tones and lights and requires a user to repeat the sequence. If the user succeeds, the series becomes progressively longer and more complex. Once the user fails or the time limit runs out, the game is over. Read in wikipedia.

This android game is a simulation of that. 

The buttons on the screen glow in some order with their notes. The user must reproduce these button clicks in the same order. 

It is a very good game for memory with the added benefit of audio feature. 

The source code of the game is available in my github page Simon 

The android game with Simon and 3 more memory games by Hegdeapps can be downloaded from google play 



Basically in our layout file, we have four SimonCell custom widgets which are extended classes of image views. These are the four colored buttons you see in the image above. 


            <com.hegdeapps.simonsays.SimonCell
                android:id="@+id/cell1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                android:src="@drawable/green" />

Now this SimonCell has on and off states. When it is in On state, the glowing button is set as drawable. Here is the SimonCell.java

public class SimonCell extends androidx.appcompat.widget.AppCompatImageView
{
    private final Context mContext;
    final static int ON = 1;
    final static int OFF = 2;
    public final static  int CELL_TYPE_RED = 1;
    public final static  int CELL_TYPE_BLUE = 2;
    public final static  int CELL_TYPE_GREEN = 3;
    public final static  int CELL_TYPE_YELLOW = 4;
    public  int onDrawable;
    public int offDrawable;
    int onColor;
    int offColor;
    int color;
    int state;

    int cellType;

    int soundRawFile;
    void setOn()
    {
       
        this.setImageDrawable(getResources().getDrawable(onDrawable));
        this.refreshDrawableState();
    }
    public SimonCell(Context ctx,AttributeSet attr){
        super(ctx,attr);
        mContext =ctx; 
    }
    public SimonCell(Context ctx){
        super(ctx);
        mContext =ctx; 
    }
    void onCellClicked()
    {
        if(getState()!=ON) {
            setOn();
        }
     }
    public int getState() {
        return state;
    }
    void setOff() {
        state = OFF;
        this.setImageDrawable(getResources().getDrawable(offDrawable));

        this.refreshDrawableState();
    }

    public void setCellType(int cellType) {
        this.cellType = cellType;
        switch (this.cellType) {
            case CELL_TYPE_BLUE:
                this.onDrawable = R.drawable.blueon;

                offDrawable = R.drawable.blue; 
                break;
            case CELL_TYPE_GREEN:
                onDrawable = R.drawable.greenon;

                offDrawable = R.drawable.green; 
                break;
            case CELL_TYPE_RED:
                onDrawable = R.drawable.redon;
                offDrawable = R.drawable.red; 
                break;
            case CELL_TYPE_YELLOW:
                onDrawable = R.drawable.yellowon;
                offDrawable = R.drawable.yellow; 
                break;
        }
    }
} 
 
In the activity file, we store the four buttons as an array. In the start game function, we generate a random number between 0 to 3, and add this random number to the CellOnList and glow the buttons of this list accompanied by their music notes. This is taken care by a timer task - at the end of timer, if the index is less than array length, the glowonecell function is called for the next list element.

 fun glowCells2() {
        mInstancesSaved = false
        mCurrentCellIndex = 0
        // int duration = 1000 * mCellOnList.size();
        if (mTimer != null) {
            mTimer!!.cancel()
        }
        mTimer = Timer()
        mTimerTask = object : TimerTask() {
            override fun run() {
                runOnUiThread {
                    mTimerTaskCompleted = false
                    if (mCurrentCellIndex < mCellOnList!!.size) {
                        glowOneCell(mCellOnList!![mCurrentCellIndex++])
                        
                    } else {
                        mTimerTaskCompleted = true
                    }
                }
            }
        }
        mTimer!!.schedule(mTimerTask, 0, 700)
    }
Glowonecell will display ondrawable, play the note of the cell and starts the timer. At the end of the timer, the cell will display the normal off drawable.

  private fun glowOneCell(celIndex: Int) {
        val cell = mImgView[celIndex]
        if (mPrevSoundStreamID != 0) {
            soundPool!!.stop(mPrevSoundStreamID)
        }
        if (mSoundOn) {
            mPrevSoundStreamID =
                soundPool!!.play(mSoundID[celIndex], mSndVolume, mSndVolume, 1, 0, 1f)
        }
        cell!!.setOn()
        startTimer(cell)
    }
      private fun startTimer(cell: SimonCell?) {
        val timer: CountDownTimer = object : CountDownTimer(300, 400) {
            override fun onTick(l: Long) {}
            override fun onFinish() {
                cell!!.setOff()
            }
        }
        timer.start()
    }
    
Now so far, we see how the game displays buttons in order. Next let us see, how do we check if the user has pressed the buttons in correct order. So the buttons have onclicklisteners
  for (i in 0..3) {
            mImgView[i]!!.setOnClickListener(this)
            mImgView[i]!!.setOff()
        }
           
And onclick method will call processcellclicked

 override fun onClick(view: View) {
        val id = view.id

        when (id) {
            R.id.cell1 -> processCellClicked(0, view as SimonCell)
            R.id.cell2 -> processCellClicked(1, view as SimonCell)
            R.id.cell3 -> processCellClicked(2, view as SimonCell)
            R.id.cell4 -> processCellClicked(3, view as SimonCell)

        }
    }
    
    private fun processCellClicked(cellNum: Int, cell: SimonCell) {
        if (mTimerForClick != null) {
            mTimerForClick!!.cancel()
        }
        cell.setOn()
        if (mPrevSoundStreamID != 0) {
            soundPool!!.stop(mPrevSoundStreamID)
        }
        if (mSoundOn) {
            mPrevSoundStreamID =
                soundPool!!.play(mSoundID[cellNum], mSndVolume, mSndVolume, 1, 0, 1f)
        }
        startTimer(cell)
        if (mCellOnList!![count] != cellNum) {
            showAlert("Game Over\n\nScore $mScore", GAME_OVER)
            return
        }
        count++
        if (count == mLevel) {
            mLevel++
            count = 0
            mScore += 1000
            showScore()
            saveScoreInPref()
            showAlert(
                "LEVEL " + (mLevel - 1).toString() + " COMPLETED\n\nSCORE " + mScore,
                LEVEL_COMPLETED
            )
        }
    }
    

ProcessCellClicked method glows the cell using setcellon method. If the previous note is playing, it is stopped and and then the note is played. If the button pressed in not in the correct order then the Game over message is displayed and game stops. And if the end of list is reached and all buttons are in correct order, then level is incremented and next level is started. 

Note : The button color and note playing can be added as properties of the cell. And of course UI can be improved :)

 

Comments