' Make it with Micromite (Part 8)

' Dice Prediction Game V1

' Code written: July 2019





' Uses the Electronic Dice hardware, plus a piezo sounder (between Pins 26 and 22)





' NOTE: DO NOT hold down the key when guessing - just a brief, single tap for each guess!

' =====



' ------------------------------------------------------------------------------------------------------------------------

' ADDITIONAL SOFTWARE NOTE:

' =========================

'

'   In the Main-Program below, the ASC command is used. It is utilised in a simple formula to convert the key-press

'   (i.e. a character from '1' to '6') into a matching number (1 to 6) and load this value into variable 'Guess'.

'   The ASC command returns the ASCII numeric value for a character, or keypress. (Google: 'ASCII code table').

'   For character '1' the ASCII value is 49, '2' is 50, '3' is 51, and so on up to character '6' which is ASCII value 54.

'   So, by subtracting 48 from the ASCII (i.e. ASC) value, we end up with a relevant numeric value representing the guess.

' ------------------------------------------------------------------------------------------------------------------------





' SETUP

' =====

'

 Option Autorun on                               ' ensure program starts automatically on power-up

 SetPin 4,DOUT : SetPin 5,DOUT : SetPin 6,DOUT   ' set three Digital OUTputs for driving the Dice Module LEDs

 SetPin 22,DOUT                                  ' 0V piezo supply for when using the recommended piezo

 Tempo=1                                         ' 1 = normal speed (increase value to speed up tune)



 Blank                                           ' call 'SUB Blank' to ensure the dice LEDs are all switched off

 SetTick 1,LEDdisp                               ' every 1ms go update dice LED-pins (persistence of vision)



' Notes as Frequencies (Hz)

 Const C4=262,D4=294,E4=330,F4=349,G4=392,A4=440,B4=494  ' fourth octave frequency values

 Const C5=523,D5=587,E5=659,F5=698,G5=784,A5=880,B5=988  ' fifth octave frequency values

 Const pp=0                                              ' represents a silent note!



' Tune Data

 Tune_Correct:                                   ' label pointer (used by RESTORE) for 'correct guess' tune

  Data C4,8,C4,8,C4,8,D4,8,C4,8,D4,8,E4,2,pp,999 ' Note & Duration 'pairs' of data representing the tune



 Tune_Incorrect:                                 ' label pointer for 'incorrect guess' tune

  Data E4,8,pp,16,C4,2,pp,999



 Tune_Invalid:                                   ' label pointer for 'inalid key-press' tune

  Data B4,32, pp,999



 Tune_Reset:                                     ' label pointer for 'reset game' tune

  Data G4,16, C4,16, G4,16, C4,16, pp,999





' MAIN PROGRAM

' ============

'

LED_CountOneToSix                                ' call SUB to shown dice patterns 1 to 6 on LEDs

StartOfGame:                                     ' a label (ends in semicolon) to create a 'jump to' point

 DrawConsoleScreen                               ' call SUB to draw game-screen on console - (and display scores as 0)

 EnterGuess:                                     ' a label to create another 'jump to' point

  a$=Inkey$                                      ' see if any key is being pressed with the INKEY$ command - load into a$

  If a$="" Then GoTo EnterGuess                  ' if no key pressed then go back to 'EnterGuess' (label) and check again

  Select Case a$ 'Guess                          ' if here, then a key has been pressed, so, check what key was pressed

    Case "1","2","3","4","5","6"                   ' if a valid key (i.e. "1" to "6") then it is a valid prediction

      Guess = Asc(a$)-48                             ' extract value (1-6) from ASCII code of key pressed - store in 'Guess'

      DrawGoodLuck                                   ' call SUB to update screen with 'Good Luck' message



    Case "r","R"                                   ' if 'r' (or 'R' i.e. Caps Lock on), then reset game

      Blank                                          ' ensure dice LEDs switched off

      GoTo StartOfGame                               ' go to 'StatOfGame' label to begin a new game



    Case Else                                      ' if here, then an invalid key has been pressed

      Print Chr$(27)+"[37m";                               ' change font colour to dim white

      Print "INVALID: (Must be a number between 1 and 6)"  ' display a message showing guess was 'invalid'

      Print Chr$(27)+"[0m";                                ' reset colour attributes

      Restore Tune_Invalid                                 ' point to 'Invalid' tune DATA

      playtune                                             ' call SUB to play tune in DATA being pointed to by RESTORE

      GoTo EnterGuess                                      ' go to 'EnterGuess' to wait for next key press

    End Select

 ThrowDice                                       ' if here then Guess is a value from 1 to 6 so call SUB to roll dice

 CheckResult                                     ' call SUB to check the rolled dice value against the guessed value

 GoTo EnterGuess                                 ' go back to play next turn

End





' SUBROUTINES

' ===========

'

Sub LEDdisp                                      ' SUB to display next dot-pair each time this SUB is called (every 1ms)

  Select Case LEDctr                               ' see which of the three dot-pairs to show based on LEDctr value

    Case 0: Port(4,3)=s1                             ' output relevant LED dot-pair data on to pins 4, 5, and 6

    Case 1: Port(4,3)=s2

    Case 2: Port(4,3)=s3

  End Select

  LEDctr=LEDctr+1                                  ' increment LEDctr value

  If LEDctr > 2 Then LEDctr = 0                    ' when LEDctr = 3, then reset its value to 0

End Sub





Sub Blank                                        ' SUB to load s1, s2, s3 (which are automatically used by SUB LEDdisp)

  s1=&b111:s2=&b111:s3=&b111                       ' setting the 3 LED output pins high will switch off all dice LEDs

End Sub





Sub LED_CountOneToSix                            ' SUB to display initial count of 1 to 6 on LEDs (at Power-Up)

  For diceNum=1 To 6                               ' FOR-NEXT loop from 1 to 6 (with the value in variable 'diceNum')

    LoadDotPairs                                     ' call SUB to load relevant values for s1, s2, s3 (for SETTICK)

    Pause 200                                        ' pause a short while (i.e. 200ms)

  Next diceNum                                       ' repeat - all the way up to '6'

  Blank                                            ' finally, ensure all LEDs are switched off

End Sub





Sub LoadDotPairs                                ' SUB to load the correct 'dot-pair' values depending on diceNum value

  Select Case diceNum                             ' jump to relevant set of dot-pair values

    Case 1 : s1=&b101 : s2=&b111 : s3=&b111         ' use upto 3 'dot-pairs' (s1, s2, s3) to make correct pattern

    Case 2 : s1=&b010 : s2=&b000 : s3=&b000         ' D3+D5 ...       ...

    Case 3 : s1=&b101 : s2=&b100 : s3=&b111         ' D4    ... D1+D7 ...

    Case 4 : s1=&b010 : s2=&b100 : s3=&b000         ' D3+D5 ... D1+D7 ...

    Case 5 : s1=&b100 : s2=&b101 : s3=&b010         ' D1+D7 ...  D4   ... D3+D5

    Case 6 : s1=&b100 : s2=&b010 : s3=&b011         ' D1+D7 ... D3+D5 ... D2+D6

  End Select

End Sub





Sub Spin                                        ' SUB to dimly sequence the 3 pairs of LEDs to give a single 'spin effect'

  Port(4,3)=&b010 : Pause 1 : Blank : Pause 50    ' Bottom-Left AND Top-Right LEDs on (D3, D5)

  Port(4,3)=&b011 : Pause 1 : Blank : Pause 50    ' Middle-Left AND Middle-Right LEDs on (D2, D6)

  Port(4,3)=&b100 : Pause 1 : Blank : Pause 50    ' Top-Left, Bottom-Right (D1, D7)

End Sub





Sub DrawConsoleScreen                           ' SUB to draw the DPG screen - uses multiple ESC codes

  Print Chr$(27)+"[0m";                           ' reset all attributes

  Print Chr$(27)+"[2J";                           ' clear screen

  Print Chr$(27)+"[H";                            ' place cursor at top-left (home) position

  Print                                           ' prints a 'blank' line



  Print Chr$(27)+"[1;33m";                        ' change font colour to bright yellow

  Print "      Dice Prediction Game"

  Print "     ======================"

  Print

  Print Chr$(27)+"[0m";                           ' reset all attributes



  Print Chr$(27)+"[37m";                          ' change font colour to dim white

  Print " Guess the next roll of the dice"

  Print " by pressing a number key  (1-6)"



  Print Chr$(27)+"[36m";                          ' change font colour to dim cyan

  Print

  Print "   [Press 'R' to Reset score]"

  Print

  Print Chr$(27)+"[0m";                           ' reset all attributes



  Print "     ATTEMPTS      CORRECT"

  Print "     --------      -------"

  attempts=0 : correct=0 : updatescore            ' set 'attempts' & 'correct' values to 0; call SUB to display values



  DrawPredict                                     ' call SUB to display 'predict now' message



  Restore Tune_Reset : PlayTune                   ' point to, and then play 'reset' tune

End Sub





Sub DrawPredict                                   ' SUB to display a 'Predict Now' message (to prompt for key press)

  Print Chr$(27)+"[14;8H";                          ' cursor to line 14, column 8

  Print Chr$(27)+"[J";                              ' clear to bottom of screen (to delete any previous content)

  Print "   Predict now!"                           ' display message

End Sub





Sub DrawGoodLuck                                  ' SUB to display a 'Good Luck' message (and also show what was predicited)

  Print Chr$(27)+"[14;8H";                          ' cursor to line 14, column 8

  Print Chr$(27)+"[J";                              ' clear to bottom of screen

  Print "    Good Luck! "



  Print Chr$(27)+"[36m";                            ' change font colour to dim cyan

  Print "       (You predicted "; Str$(Guess); ")"  ' show the value predicated (stored in variable 'Guess')

  Print Chr$(27)+"[0m";                             ' reset all attributes

End Sub





Sub UpdateScore                                   ' SUB to update score values (in 'attempts' & 'correct' variables)

  Print Chr$(27)+"[H";                              ' cursor home

  Print Chr$(27)+"[1;31;40m";                       ' bright red font on black background

  Print Chr$(27)+"[12;8H";                          ' cursor to line 12, column 8

  Print Str$(attempts,3)                            ' uses STR$ command to format 'attempts' to display up to 3 digits



  Print Chr$(27)+"[32;40m";                         ' green font on black background

  Print Chr$(27)+"[12;22H";                         ' cursor to line 12, column 22

  Print Str$(correct,3)                             ' display 'correct' score (up to 3 digits)

  Print Chr$(27)+"[0m";                             ' reset attributes

End Sub





Sub ThrowDice                                     ' SUB to simulate rolling the dice

  Randomize Timer                                   ' makes sure RND sequence doesn't repeat after power-up

                                                    ' COMMENT OUT above line to convert Prediction Game into a MEMORY Game

                                                    ' (remember to press DM Reset button to start MEMORY Game at beginning)



  diceNum = Int(Rnd(0)*6)+1                         ' generate a random number between 1 and 6

  spinNum = Int(Rnd(0)*8)+8                         ' generate a random number (8-15) for how long to show spin effect

  For x=0 To spinNum                                ' loop the spin effect for the required number of 'spins'

    spin                                              '  (...liken this to how hard the dice is being thrown!)

  Next x

  Select Case diceNum                               ' use diceNum value to load correct 'dot-pair' values into s1, s2, s3

    Case 1 : s1=&b101 : s2=&b111 : s3=&b111           ' use upto 3 'dot-pairs' (s1, s2, s3) to display the correct pattern

    Case 2 : s1=&b010 : s2=&b000 : s3=&b000           ' D3+D5 ...       ...

    Case 3 : s1=&b101 : s2=&b100 : s3=&b111           ' D4    ... D1+D7 ...

    Case 4 : s1=&b010 : s2=&b100 : s3=&b000           ' D3+D5 ... D1+D7 ...

    Case 5 : s1=&b100 : s2=&b101 : s3=&b010           ' D1+D7 ...  D4   ... D3+D5

    Case 6 : s1=&b100 : s2=&b010 : s3=&b011           ' D1+D7 ... D3+D5 ... D2+D6

  End Select

End Sub





Sub CheckResult                                   ' SUB to compare player's 'Guess' with rolled 'diceNum'

  If diceNum=Guess Then                             ' see if same...

    Restore Tune_Correct                              ' if same, then point to 'Correct' tune DATA

    correct=correct+1                                   ' and also increase 'correct' score

  Else                                              ' if not same...

    Restore Tune_Incorrect                            ' then point to 'Incorrect' tune

  End If

  attempts=attempts+1                               ' increment value of 'attempts'

  playTune                                          ' play tune (pointed to by whichever RESTORE above)

  updatescore                                       ' update score on the console display

  DrawPredict                                       ' show message to prompt for next guess

End Sub





Sub PlayTune                                      ' SUB to play tune DATA (until end-of-tune marker reached)

  Do

    Read Note,Duration                              ' load pair of note data into variables Note and Duration

    If Duration=999 Then Exit Do                    ' Exit if reached end-of-tune marker (i.e if value = 999 for Duration)

    If Note>20 Then PWM 2,Note,50                   ' If valid note then play NOTE with PWM at 50% duty (i.e. square wave)

    Pause 1000/(Duration*Tempo)                     ' play note for relevant duration

    PWM 2,stop                                      ' . . . then stop note (i.e. switch PWM off)

    Pause 10/Tempo                                  ' slight pause between each note

  Loop                                              ' go get next pair of values for Note & Duration

End Sub