|
| 1 | +// |
| 2 | +// MASTERMIND II |
| 3 | +// STEVE NORTH |
| 4 | +// CREATIVE COMPUTING |
| 5 | +// PO BOX 789-M MORRISTOWN NEW JERSEY 07960 |
| 6 | +// |
| 7 | +// Converted to MiniScript by Joe Strout (Sep 2023) |
| 8 | + |
| 9 | +// Advance the given combination to the next possible combination. |
| 10 | +// Note that (unlike the BASIC program) our values are 0-based, not 1-based. |
| 11 | +incrementCombo = function(combo) |
| 12 | + idx = 0 |
| 13 | + while true |
| 14 | + combo[idx] += 1 |
| 15 | + if combo[idx] < colorCount then return |
| 16 | + combo[idx] = 0 |
| 17 | + idx += 1 |
| 18 | + end while |
| 19 | +end function |
| 20 | + |
| 21 | +// Convert a numeric combination like [2, 0, 1] into a color string like "RBW". |
| 22 | +comboToColorString = function(combo) |
| 23 | + result = "" |
| 24 | + for q in combo |
| 25 | + result += colorCodes[q] |
| 26 | + end for |
| 27 | + return result |
| 28 | +end function |
| 29 | + |
| 30 | +// Get a random color code like "RBW". |
| 31 | +getRandomCode = function |
| 32 | + // We do this by starting with a numeric combo of all 0's (first color), |
| 33 | + // and then stepping through subsequent combos a random number of times. |
| 34 | + combo = [0] * posCount |
| 35 | + steps = floor(possibilities * rnd) |
| 36 | + while steps |
| 37 | + incrementCombo combo |
| 38 | + steps -= 1 |
| 39 | + end while |
| 40 | + return comboToColorString(combo) |
| 41 | +end function |
| 42 | +
|
| 43 | +// Return [blackPins, whitePins] for the given guess and actual code. |
| 44 | +// blackPins is how many guess entries are the correct color AND position; |
| 45 | +// whitePins is how many guess entries have the right color, but wrong position. |
| 46 | +// This works with either color strings or numeric combos. |
| 47 | +calcResult = function(guess, actual) |
| 48 | + if guess isa string then guess = guess.split("") else guess = guess[:] |
| 49 | + if actual isa string then actual = actual.split("") else actual = actual[:] |
| 50 | + black = 0; white = 0 |
| 51 | + for i in guess.indexes |
| 52 | + if guess[i] == actual[i] then |
| 53 | + black += 1 |
| 54 | + actual[i] = null |
| 55 | + else |
| 56 | + for j in actual.indexes |
| 57 | + if guess[i] == actual[j] and guess[j] != actual[j] then |
| 58 | + white += 1 |
| 59 | + actual[j] = null |
| 60 | + break |
| 61 | + end if |
| 62 | + end for |
| 63 | + end if |
| 64 | + guess[i] = null |
| 65 | + end for |
| 66 | + return [black, white] |
| 67 | +end function |
| 68 | +
|
| 69 | +// Pad a string with spaces, to the given width. |
| 70 | +pad = function(s, width=12) |
| 71 | + return (s + " "*width)[:width] |
| 72 | +end function |
| 73 | +
|
| 74 | +// Print the history of guesses and results. |
| 75 | +printBoard = function |
| 76 | + print |
| 77 | + print "BOARD" |
| 78 | + print "Move Guess Black White" |
| 79 | + for i in guessHistory.indexes |
| 80 | + print pad(i+1, 9) + pad(guessHistory[i], 15) + |
| 81 | + pad(guessResult[i][0], 10) + guessResult[i][1] |
| 82 | + end for |
| 83 | + print |
| 84 | +end function |
| 85 | +
|
| 86 | +// Quit the game (after reporting the computer's secret code). |
| 87 | +quit = function(computerCode) |
| 88 | + print "Quitter! My combination was: " + computerCode |
| 89 | + print |
| 90 | + print "Good bye" |
| 91 | + exit |
| 92 | +end function |
| 93 | + |
| 94 | +// Prompt the user for a guess (e.g. "RBW"). |
| 95 | +// Also handle "BOARD" and "QUIT" commands. |
| 96 | +inputGuess = function(guessNum, secretCode) |
| 97 | + while true |
| 98 | + guess = input("Move #" + guessNum + " Guess? ").upper |
| 99 | + if guess == "BOARD" then |
| 100 | + printBoard |
| 101 | + else if guess == "QUIT" then |
| 102 | + quit secretCode |
| 103 | + else if guess.len != posCount then |
| 104 | + print "Bad number of positions." |
| 105 | + else |
| 106 | + ok = true |
| 107 | + for c in guess |
| 108 | + if colorCodes.indexOf(c) == null then |
| 109 | + print "'" + c + "' is unrecognized." |
| 110 | + ok = false |
| 111 | + break |
| 112 | + end if |
| 113 | + end for |
| 114 | + if ok then return guess |
| 115 | + end if |
| 116 | + end while |
| 117 | +end function |
| 118 | + |
| 119 | +// Play one half-round where the computer picks a code, |
| 120 | +// and the human tries to guess it. |
| 121 | +doHumanGuesses = function |
| 122 | + print "Guess my combination." |
| 123 | + print |
| 124 | + secretCode = getRandomCode |
| 125 | + //print "My secret combo is: " + secretCode // (for debugging purposes) |
| 126 | + |
| 127 | + globals.guessHistory = [] // list of guesses, e.g. "RBW" |
| 128 | + globals.guessResult = [] // result of each guess, as [BlackPins, WhitePins] |
| 129 | + |
| 130 | + for guessNum in range(1, 10) |
| 131 | + guess = inputGuess(guessNum, secretCode) |
| 132 | + result = calcResult(guess, secretCode) |
| 133 | + if result[0] == posCount then |
| 134 | + print "You guessed it in " + guessNum + " moves!" |
| 135 | + break |
| 136 | + end if |
| 137 | + // Tell human results |
| 138 | + print "You have " + result[0] + " blacks and " + result[1] + " whites." |
| 139 | + // Save all this stuff for board printout later |
| 140 | + guessHistory.push guess |
| 141 | + guessResult.push result |
| 142 | + end for |
| 143 | + if guess != secretCode then |
| 144 | + print "You ran out of moves! That's all you get!" |
| 145 | + print "The actual combination was: " + secretCode |
| 146 | + end if |
| 147 | + globals.humanScore += guessNum |
| 148 | +end function |
| 149 | + |
| 150 | +// Play one half-round where the human picks a code, |
| 151 | +// and the computer tries to guess it. |
| 152 | +// Return true if this goes OK, and false if we need a do-over. |
| 153 | +doComputerGuesses = function |
| 154 | + print "Now I guess. Think of a combination." |
| 155 | + input "Hit Return when ready:" |
| 156 | + |
| 157 | + // Make a list of possible combination *numbers*, from 0 to possibilities-1. |
| 158 | + // We'll remove entries from this list as we eliminate them with our guesses. |
| 159 | + possible = range(0, possibilities-1) |
| 160 | + |
| 161 | + gotIt = false |
| 162 | + for guessNum in range(1, 10) |
| 163 | + if not possible then |
| 164 | + print "You have given me inconsistent information." |
| 165 | + print "Try again, and this time please be more careful." |
| 166 | + return false |
| 167 | + end if |
| 168 | + guessIdx = possible[rnd * possible.len] |
| 169 | + guessCombo = [0] * posCount |
| 170 | + if guessIdx > 0 then |
| 171 | + for x in range(0, guessIdx-1) |
| 172 | + incrementCombo guessCombo |
| 173 | + end for |
| 174 | + end if |
| 175 | + |
| 176 | + print "My guess is: " + comboToColorString(guessCombo) |
| 177 | +
|
| 178 | + while true |
| 179 | + s = input(" Blacks, Whites? ").replace(",", " ").replace(" ", " ").split |
| 180 | + if s.len == 2 then break |
| 181 | + end while |
| 182 | + actualResult = [s[0].val, s[1].val] |
| 183 | +
|
| 184 | + if actualResult[0] == posCount then |
| 185 | + print "I got it in " + guessNum + " moves!" |
| 186 | + gotIt = true |
| 187 | + break |
| 188 | + end if |
| 189 | +
|
| 190 | + // Now zip through all possibilities, and if it's still in our |
| 191 | + // possible list but doesn't match the given result, remove it. |
| 192 | + combo = [0] * posCount |
| 193 | + for x in range(0, possibilities-1) |
| 194 | + if x > 0 then incrementCombo combo |
| 195 | + idx = possible.indexOf(x) |
| 196 | + if idx == null then continue // (already eliminated) |
| 197 | + result = calcResult(combo, guessCombo) |
| 198 | + if result != actualResult then |
| 199 | + //print "Eliminating #" + x + ", " + comboToColorString(combo) |
| 200 | + possible.remove idx |
| 201 | + end if |
| 202 | + end for |
| 203 | + end for |
| 204 | + |
| 205 | + if not gotIt then |
| 206 | + print "I used up all my moves!" |
| 207 | + print "I guess my CPU is just having an off day." |
| 208 | + end if |
| 209 | + globals.computerScore += guessNum |
| 210 | + return true |
| 211 | +end function |
| 212 | +
|
| 213 | +// Show the score (with the given header). |
| 214 | +showScore = function(header="Score") |
| 215 | + print header + ":" |
| 216 | + print " COMPUTER " + computerScore |
| 217 | + print " HUMAN " + humanScore |
| 218 | + print |
| 219 | +end function |
| 220 | +
|
| 221 | +// Initialization of global variables |
| 222 | +colorCodes = "BWRGOYPT" |
| 223 | +colorNames = "Black,White,Red,Green,Orange,Yellow,Purple,Tan".split(",") |
| 224 | +computerScore = 0 |
| 225 | +humanScore = 0 |
| 226 | +
|
| 227 | +// Main program |
| 228 | +print " "*30 + "Mastermind" |
| 229 | +print " "*15 + "Creative Computing Morristown, New Jersey" |
| 230 | +print; print; print |
| 231 | +while true |
| 232 | + colorCount = input("Number of colors? ").val |
| 233 | + if 0 < colorCount <= 8 then break |
| 234 | + print "No more than 8, please!" |
| 235 | +end while |
| 236 | +posCount = input("Number of positions? ").val |
| 237 | +roundCount = input("Number of rounds? ").val |
| 238 | +possibilities = colorCount ^ posCount |
| 239 | +print "Total possibilities = " + possibilities |
| 240 | +
|
| 241 | +print; print |
| 242 | +print "Color Letter" |
| 243 | +print "===== ======" |
| 244 | +for x in range(0, colorCount-1) |
| 245 | + print pad(colorNames[x], 9) + colorCodes[x] |
| 246 | +end for |
| 247 | +print |
| 248 | +
|
| 249 | +for round in range(1, roundCount) |
| 250 | + print |
| 251 | + print "Round number " + round + " ----" |
| 252 | + print |
| 253 | + doHumanGuesses |
| 254 | + showScore |
| 255 | + while not doComputerGuesses; end while |
| 256 | + showScore |
| 257 | +end for |
| 258 | +
|
| 259 | +print "GAME OVER" |
| 260 | +showScore "Final score" |
0 commit comments