Master Mind Game: Guess Scoring Program

Description

Press f LBL A to start the program.
  1. You see "0," appearing on the HP screen.
  2. Enter a number 'N' from 1 to 6. Press R/S
    This represents both the number of digits in the number to guess as the maximum value for each of the digits. The minimum value for each digit is always 1.
  3. The program then constructs an N digit random 'secret' number S.
  4. The user now has to enter an N digit integer 'guess' number G. Press R/S.
  5. The program 'scores' the guess by how many digit couples taking one from S and one from G on the same position are identical, we call this 'c' (from correct) and how many of the remaining digit couples (one from G and one from S, but in different positions) are the same, we call this 'd' (from displaced). The program returns g,cd in the display. Note that c+d <= N always holds.
  6. Goto 4) [until in 5) you get an answer where G,cd looks like G,N0 with N the number of digits to entered, because that means that your guess G = the secret S and all N digits are in the right position (c==N, and none in the wrong position, d=0)].
Example:

N = 4
Secret S = 2243
Guess G = 4213
Answer on HP display = 4223,21
c = 2 since the 2 and the 3 match on positions 2 and 4 and
d = 1 since from the leftover digits, 4 occurs in position 3 in the secret and in position 1 in the guess.
The 1 in the guess (on position 3) does not occur in the secret, so the user gets no score for that. This is also the cuplrit for the fact that c+d here and not c+d=N.

Note: It is possible for the user to enter digits > N. They will be compared as well, but just never lead to a score for that digit, nor for c nor for d.

* To help with understanding the HP assembly code, the Python equivalent of the two checking functions calculating c and d, in its basic shape is shown here.
def calculate_n_correctly_positioned_digits(
    secret, guess, matched_secret, matched_guess):
    c = 0
    for i in range(0, N):
        if guess[i] == secret[i]:
            matched_secret[i] = '1'
            matched_guess[i] = '1'
            c += 1
    return c

def calculate_n_displaced_digits(
    secret, guess, matched_secret, matched_guess):
    d = 0
    for j in range(0, N):
        for k in range(0, N):
            if matched_secret[j] == '0':
                if matched_guess[k] == '0':
                    if secret[j] == guess[k]:
                        matched_secret[j] = '1'
                        matched_guess[k]  = '1'
                        d += 1
    return d

However, after (affine) loop (index) transformation we come to this single unified function, calculating both c and d, with only two nested (be it transformed) loops.
def calculate_all_digits(secret, guess, check_index_order=True):
    c = 0
    d = 0
    for diag in range(0, N):
        for on_diag in range(0, N):
            j = on_diag % N # say vertical
            k = (diag + on_diag) % N # say horizontal
            if matched_secret[j] == '0':
                if matched_guess[k] == '0':
                    if secret[j] == guess[k]:
                        matched_secret[j] = '1'
                        matched_guess[k]  = '1'
                        if j==k:
                            c += 1
                        else:
                            d += 1
    return c,d


Note the the Python code above uses offset 0 in all indices, while the HP-15C code uses offset 1 in all indices (since R[0] and R[.0] are already used for other purposes).

Notes:
Have fun,
Peter Sels

Program Resources

Labels

Name Description Name Description
 A Master Mind program entry point. N (the number of digits is entered and when 7<=N, a re-entry is required via jump back to LBL A, via GTO A on line 017. This describes L001-L017. .1 Is Label .1: Jump forward label (to skip to over loop body or arrive at after loop body) belonging to end of outer (j=R[7] counter, LBL 6 controlled) loop. This describes lines L167 to L193.
 1 Loop jump back to label for program generating N random numbers (collectively the 'secret' number s). The N digits are stored in the registers R[1] to R[N]. This describes L018-L035. .2 Is Label .2: Jump forward label (to skip to over loop body or arrive at after loop body) belonging to end of inner (k=R[8] counter, LBL 7 controlled) loop. This describes lines L156 to L166.
 2 Non-loop jump to label for the part where the user enters an N digits collectively in 1 single 'guess' number g, the integer part of which (on line L039) is stored in R[.0] (=R[10]). (R[9] is used as a backup to be restored from into R[.0]later.) This describes L036 to L041. .3 Is Label .3: Wrapper subroutine to be able to reuse (and so call) Subroutine .7. This describes lines L194 to L198.
 3 Jump back to label for code spreading digits of guess, stored in R[.0] to R[.1] to R[.N] (for easy comparison later to the secret number in R[1] to R[N].) In this process (of sequentially dividing by 10, taking the fractional part only, multiplying by 10 to isolate and fetch each digits separetely and then store it) R[.0] is destroyed and 0 remains in it in the end. But R[.] is reloaded form the backed up value in R[9] on lines L71 and L072. This describes line L042-L071. .4 Is Label .4: Subroutine to decrement x register by N, in lines 199 to L202. This is called conditionally from the modulo calculation in the (affine) loop (index) transformation code that transforms the index couple (R[7]=diagonal index number,R[8]=on_diagonal index number) towards (j=R[8]=row number, k=(( R[.8]=R[7]+R[8]-1) modulo N(in R[0]) ) = column number). So R[7] and R[8] are used as loop counters, while the derived/transformed R[8] and R[.8] are used as the final row and column indices to index secret and guess numbers R[8] and R[.8] are used as the final row and column indices to index secret and guess numbers respectively so as to come up with digits-couples to compare in an order that respects the data dependencies in the algorithm. This order is: do all comparisons on the diagonal (where j==k) before doing any not on the diagonal (where j!=k).
 6 Loop jump back to label for outer loop of two loops (inner loop controlled by label 7) comparing the N guess digit-couples (one from secret and one from guess) on being equal (on same and different positions), couple by couple, but excluding the ones already matched ones. This describes L087 to L175. .5 Is Label .5: Code piece in lines L203 to L207 to increment cd by 10 (so effectively: c by 1) and jump back. Note that this is not a real subroutine, since it is called in 1 place only so can jump back to a fixed label (LBL .2 here) which is over the else branch of the condition (j==k). The else branch of this condition (j!=k) is to increment cd by 1 (so effectively d by 1), which is on lines L154 and L155. They come together again in label .2 on line L156.
 7 Loop jump back to label used in the inner loop within the label 6 controlled loop. Label 6 controls the loop iterating over the secret digits, while label 7 controls the loop iterating over the guess digits. This describes lines L090 to L166. .6 Is Label .6: Subroutine that shows jk,cd where jk is the index couple of secret index and guess index being currently compared in the scoring algorithm and cd is the score being accumulated up to now. This is only shown in debugging mode. (Activated by R[.7], which contains the argument to the program (the value in the stack register (so display) x when the program is started by the user. When this value is 0 you run it in normal mode, otherwise in debug mode.) This describes lines L208 to L222.
.0 Is Label .0: Loop jump back to label for the part where we reset the guess digit registers (R[.1] to R[.N] to their integer part. The fractional part is either ,0 or ,1 generated later in the program, to indicate and keep track of whether this digit was already matched (,1) with a digit of the secret number or not (,0). Obviously we need to reset this fractional part to ,0 before running the scoring algorithm again. Note that this needs to be done for the digits or the secret number (in R[1] to R[6 (at most)]), but not for the guess digits, since for them the fractional part is already ,0 since they (in registers R[.1] to R[.6 (at most)]) have just been regenerated from the new guess the user did (in lines L042 to L069, particularly line L056 containing STO (i))). Just before the resetting code, that is, on line L072 and L073, we increment R[.9] by 1, which means adding 1 to the number of turns the user has made a guess. This describes lines L072-L086.to indicate and keep track of whether this digit was already matched (,1) with a digit of the secret number or not (,0). Obviously we need to reset this fractional part to ,0 before running the scoring algorithm again. Note that this needs to be done for the digits or the secret number (in R[1] to R[6 (at most)]), but not for the guess digits, since for them the fractional part is already ,0 since they (in registers R[.1] to R[.6 (at most)]) have just been regenerated from the new guess the user did (in lines L042 to L069, particularly line L056 containing STO (i))). Just before the resetting code, that is, on line L072 and L073, we increment R[.9] by 1, which means adding 1 to the number of turns the user has made a guess. This describes lines L072-L086. .7 Is Label .7: Subroutine displaying number of current turns and then 'g.cd' where g is the guess number and .cd is the score of the guess. It is called at every guess after scoring and one more time if the guess equals the secret and so the game is over (even though one can continue guessing even then. ;) Showing results happens via pausing for a second (via the f PSE instruction ) and shows up on the display as blinking the number (currently in the x stack register) for a second. This describes lines L223 to L231.

Storage Registers

Name Description Name Description
 0 stores N, the number of digits, which is also the max value of each digit, 1 is the minimum. 1<=N<=6. .7 Stores t,a, where t is the turns the user is at and a is the original argument in X reg to mean debug mode if not 0.
 7 Stores the outer loop counter value (representing the number of the diagonal, starting at one for the real matrix diagonal and incrementing by 1 for every step up right from there) ranging from 1 to N in the scoring algorithm. Note that this is, NOT directly an index being used in indexing either secret or guess number used in the scoring algorithm to fetch digits of those numbers and being compared with each other. However, this counter will influence the k index, so the column index, since it occurs in the expression of the value in k = R[.8]. .8 Is Register .8: Stores the k variable or column variables iterating as an index into the guess number in the scoring algorithm to fetch its digits. It is calculated as a function of R7 and R8, more particularly: R[.8] = ( R[7] + R[8] -1 ) modulo N (in R[0]). As an example of the workings of the transformation from (R[7], R[8]) transformed to (R[8], R[.8]) we get as a sequence for N=2: (1,1) (1,2) (2,1) (2,2) transformed to (1,1) (2,2) (1,2) (2,1), so that indeed the diagonal elements (digits having the same position in both numbers) (1,1) (2,2) are matched and potentially marked as 'matched (and so as not-to-be-checked again)' between guess and secret before the non-diagonal elements with indices (2,1) (2,2) are checked.
 8 Stores the inner loop counter value ranging from 1 to N in the scoring algorithm. This represent the position on the diagonal from 1 to N, going down right on the diagonal. This is directly the j variable or row variable indexing the secret word to fetch its digits. This is directly the j variable or row variable indexing the secret word to fetch its digits. This is directly the j variable or row variable indexing the secret word to fetch its digits. This represent the position on the diagonal from 1 to N, going down right on the diagonal. .9 This is the number of turns t the user has guessed up to now. Its updated value will be displayed as t intermittently with the g,cd display after every guess.
 9 stores 'cd', the number 'c' of same position and matching digit-couples between guess and secret, and stores 'd', the number of digit-couples (excluding already same position ones) matching between guess and secret that match but so, are on different positions. (i) is the contents of R[I] and is used in different loops to go from R(o+1) to R(o+N) where o is an offset of 0 (for secret number) and 10 (for the guess number) and 1<=N<=6.
.0 initially stores the user entered guess number and is the split into digits by N-sequential division by 10, so that it stores 0, but then its contents has been transfered to R[.1] to R[.N]. I is the index of R[I] and is used in different loops to go from o+1 to o+N, where o is an offset of 0 (for secret number) and 10 (for the guess number) and 1<=N<=6.

Program

Line Display Key Sequence Line Display Key Sequence Line Display Key Sequence
000 078 1 1 156 42,21, .2 f LBL . 2
001 42,21,11 f LBL A 079 44,40,25 STO + I 157 45 .7 RCL . 7
002 36 ENTER 080 45 24 RCL (i) 158 43,30, 0 g TEST x≠0
003 1 1 081 43 44 g INT 159 32 .6 GSB . 6
004 9 9 082 44 24 STO (i) 160 45 0 RCL 0
005 42,23,24 f DIM (i) 083 5 5 161 1 1
006 42 34 f REG 084 45 25 RCL I 162 44,40, 8 STO + 8
007 33 R⬇ 085 43 10 g x≤y 163 33 R⬇
008 44 .7 STO . 7 086 22 .0 GTO . 0 164 45 8 RCL 8
009 43 35 g CLx 087 1 1 165 43 10 g x≤y
010 42, 7, 0 f FIX 0 088 44 7 STO 7 166 22 7 GTO 7
011 31 R/S 089 42,21, 6 f LBL 6 167 42,21, .1 f LBL . 1
012 43 44 g INT 090 1 1 168 45 0 RCL 0
013 44 0 STO 0 091 44 8 STO 8 169 36 ENTER
014 36 ENTER 092 42,21, 7 f LBL 7 170 1 1
015 7 7 093 45 0 RCL 0 171 44,40, 7 STO + 7
016 43 10 g x≤y 094 1 1 172 33 R⬇
017 22 11 GTO A 095 40 + 173 45 7 RCL 7
018 43 35 g CLx 096 36 ENTER 174 43 10 g x≤y
019 1 1 097 45 7 RCL 7 175 22 6 GTO 6
020 44 25 STO I 098 45 8 RCL 8 176 45 .0 RCL . 0
021 42,21, 1 f LBL 1 099 40 + 177 45 9 RCL 9
022 42 36 f RAN # 100 43,30, 7 g TEST x>y 178 48 .
023 45 0 RCL 0 101 32 .4 GSB . 4 179 0 0
024 20 × 102 1 1 180 1 1
025 43 44 g INT 103 30 181 20 ×
026 1 1 104 44 .8 STO . 8 182 40 +
027 40 + 105 45 8 RCL 8 183 45 9 RCL 9
028 44 24 STO (i) 106 44 25 STO I 184 1 1
029 1 1 107 45 24 RCL (i) 185 0 0
030 44,40,25 STO + I 108 42 44 f FRAC 186 10 ÷
031 45 0 RCL 0 109 43,30, 0 g TEST x≠0 187 43 44 g INT
032 45 25 RCL I 110 22 .2 GTO . 2 188 45 0 RCL 0
033 43 10 g x≤y 111 45 .8 RCL . 8 189 30
034 22 1 GTO 1 112 1 1 190 43 20 g x=0
035 43 35 g CLx 113 0 0 191 32 .3 GSB . 3
036 42,21, 2 f LBL 2 114 40 + 192 33 R⬇
037 32 .7 GSB . 7 115 44 25 STO I 193 22 2 GTO 2
038 31 R/S 116 45 24 RCL (i) 194 42,21, .3 f LBL . 3
039 43 44 g INT 117 42 44 f FRAC 195 33 R⬇
040 44 .0 STO . 0 118 43,30, 0 g TEST x≠0 196 32 .7 GSB . 7
041 44 9 STO 9 119 22 .2 GTO . 2 197 36 ENTER
042 1 1 120 45 8 RCL 8 198 43 32 g RTN
043 0 0 121 44 25 STO I 199 42,21, .4 f LBL . 4
044 45 0 RCL 0 122 45 24 RCL (i) 200 45 0 RCL 0
045 40 + 123 43 44 g INT 201 30
046 44 25 STO I 124 36 ENTER 202 43 32 g RTN
047 42,21, 3 f LBL 3 125 45 .8 RCL . 8 203 42,21, .5 f LBL . 5
048 45 .0 RCL . 0 126 1 1 204 1 1
049 1 1 127 0 0 205 0 0
050 0 0 128 40 + 206 44,40, 9 STO + 9
051 10 ÷ 129 44 25 STO I 207 22 .2 GTO . 2
052 42 44 f FRAC 130 33 R⬇ 208 42,21, .6 f LBL . 6
053 1 1 131 45 24 RCL (i) 209 45 8 RCL 8
054 0 0 132 43 44 g INT 210 1 1
055 20 × 133 30 211 0 0
056 44 24 STO (i) 134 43,30, 0 g TEST x≠0 212 20 ×
057 45 .0 RCL . 0 135 22 .2 GTO . 2 213 45 .8 RCL . 8
058 1 1 136 45 8 RCL 8 214 40 +
059 0 0 137 44 25 STO I 215 45 9 RCL 9
060 10 ÷ 138 48 . 216 48 .
061 43 44 g INT 139 1 1 217 0 0
062 44 .0 STO . 0 140 44,40,24 STO + (i) 218 1 1
063 1 1 141 45 .8 RCL . 8 219 20 ×
064 44,30,25 STO I 142 1 1 220 40 +
065 45 25 RCL I 143 0 0 221 42 31 f PSE
066 1 1 144 40 + 222 43 32 g RTN
067 0 0 145 44 25 STO I 223 42,21, .7 f LBL . 7
068 43 10 g x≤y 146 48 . 224 36 ENTER
069 22 3 GTO 3 147 1 1 225 42, 7, 0 f FIX 0
070 45 9 RCL 9 148 44,40,24 STO + (i) 226 45 .9 RCL . 9
071 44 .0 STO . 0 149 45 8 RCL 8 227 42 31 f PSE
072 1 1 150 45 .8 RCL . 8 228 42, 7, 2 f FIX 2
073 44,40, .9 STO + . 9 151 30 229 34 x↔y
074 43 35 g CLx 152 43 20 g x=0 230 42 31 f PSE
075 44 9 STO 9 153 22 .5 GTO . 5 231 43 32 g RTN
076 44 25 STO I 154 1 1
077 42,21, .0 f LBL . 0 155 44,40, 9 STO + 9