Electronic MasterMind Game
by NickZero in Circuits > Raspberry Pi
2671 Views, 46 Favorites, 0 Comments
Electronic MasterMind Game
“A battle of wits and logic between two players. Break the secret code.”
This is an electronic version of the classic 70’s Super Master Mind board game. The object is to guess the code that has been selected by the computer in as few moves as possible using clues given at each turn.
Supplies
Materials
Rasberry Pi Zero WH
SD Card
Adafruit 16x32 LED Matrix Panel (6mm pitch)
Adafruit Matrix Hat (or Bonnet)
7x F-F Dupont Wires
Small length of Stiff wire
6x 10mm Buttons
2.5mm Header block
5v 3A Power Supply
Female Socket barrel jack power connector 5.5x2.1mm
Short wire Jack plug connector male 5.5mm x 2.1mm
Brown, Transparent and Red PLA (or colours of choice)
Tools/Supplies
3D Printer
Soldering Iron
Super Glue
Tweezers/Wire Snips/Helping hands
Laptop/Computer for creating SD card and Pi Setup
Insulating Material (plastic sheet, thin cardboard or similar)
Raspberry Pi Setup
Download and install Rasbian Lite for the Pi Zero, minimal install, no GUI, all that is needed is Python.
https://www.raspberrypi.com/software/operating-systems/
The easiest way to install the OS is using the imager, here:
https://www.raspberrypi.com/software/
Change the default password, setup Wifi (optional) and SSH access – this can be done using the Raspberry Pi Installer options.
Matrix Hat
There is also a Matrix bonnet which is a bit cheaper, but was not available while I was making this.
Solder the header and connectors as per the hat assembly instructions:
https://learn.adafruit.com/adafruit-rgb-matrix-plus-real-time-clock-hat-for-raspberry-pi/assembly
I cut a thin plastic sheet to insulate the bottom of the matrix from the other components, with a cutout for the power and data cables but this is optional
Connect the hat, matrix power connectors, and data ribbon cable then power up the Pi for the next section.
Matrix Libraries Install
Download and install all of the matrix software for the Pi as per Adafruit’s instructions, at step 6 here:
Python Program and Auto Run
Create a folder in the home directory of the Pi called mastermind and save the mm-main-game.py and samplebase.py files there. Make sure the program runs by issuing the following command:
Sudo ./mm-main-game.py -r 16 -b 100
(Or you might need to specify the python3 interpreter)
Sudo /usr/bin/python3 /home/pi/mastermind/mm-main-game.py -r 16 -b 100
SUDO is needed to run the program as the matrix driver needs this to function correctly. You can run without, but you might get flickers or glitches.
The –r specifies the rows and the –b is the brightness (100 is max)
CODE BELOW. I wasnt able to attach this as a file, not sure why.
#!/usr/bin/env python ### Mastermind Game - Nick Stevens 2022 ### #libraries from samplebase import SampleBase import RPi.GPIO as GPIO import random class MasterMind(SampleBase): def __init__(self, *args, **kwargs): super(MasterMind, self).__init__(*args, **kwargs) def run(self): def answerline(pos,colour): #answerline answer_x = 2 answer_y = 6+(pos*2) self.matrix.SetPixel(answer_x,answer_y,colour[0],colour[1],colour[2]) def answerlineall(colour): #answer line answer_x = 2 answer_y = 6 while answer_y < 15: self.matrix.SetPixel(answer_x,answer_y,colour[0],colour[1],colour[2]) answer_y = answer_y + 2 answer_y = 6 def trylinesall(colour): #try lines try_x = 28 try_y = 6 while try_x > 5: while try_y < 15: self.matrix.SetPixel(try_x,try_y,colour[0],colour[1],colour[2]) try_y = try_y + 2 try_y = 6 try_x = try_x - 2 try_x = 28 def tryline(line,pos,colour): #try line try_x = 28-(line*2) try_y = 6+(pos*2) self.matrix.SetPixel(try_x,try_y,colour[0],colour[1],colour[2]) def scorelinesall(colour): #score lines score_x = 28 score_y = 0 while score_x > 5: while score_y < 5: self.matrix.SetPixel(score_x,score_y,colour[0],colour[1],colour[2]) score_y = score_y + 1 score_y = 0 score_x = score_x - 2 score_x = 28 def pulsescoreline(line): #score lines score_x = 28-(line*2) score_y = 0 colour = purple while score_y < 5: self.matrix.SetPixel(score_x,score_y,colour[0],colour[1],colour[2]) score_y = score_y + 1 self.usleep(25 * 1000) score_y = 0 colour = white while score_y < 5: self.matrix.SetPixel(score_x,score_y,colour[0],colour[1],colour[2]) score_y = score_y + 1 self.usleep(25 * 1000) def scoreline(line,pos,colour): score_x = 28-(line*2) score_y = pos self.matrix.SetPixel(score_x,score_y,colour[0],colour[1],colour[2]) #set-up variables max_brightness = self.matrix.brightness count = 0 red = [255, 0, 0] blue = [0, 0, 255] green = [0,255,0] white = [255, 255, 255] orange = [255, 131, 0] yellow = [255,255,0] aqua = [0,255,255] purple = [128,0,128] off = [0,0,0] press = False colours = [red,blue,green,white,orange,yellow,aqua,purple] answer = [red,red,red,red,red] #startup-sequence while press == False: #wait till a button is pressed for pin in button: if GPIO.input(pin) == 0: press = True if self.matrix.brightness < 1: self.matrix.brightness = max_brightness count += 1 else: self.matrix.brightness -= 1 if count % 8 == 0: #RED colour = red answerlineall(colour) trylinesall(colour) scorelinesall(white) elif count % 8 == 1: #GREEN colour = green answerlineall(colour) trylinesall(colour) scorelinesall(purple) elif count % 8 == 2: #BLUE colour = blue answerlineall(colour) trylinesall(colour) scorelinesall(white) elif count % 8 == 3: #WHITE colour = white answerlineall(colour) trylinesall(colour) scorelinesall(purple) elif count % 8 == 4: #ORANGE colour = orange answerlineall(colour) trylinesall(colour) scorelinesall(white) elif count % 8 == 5: #YELLOW colour = yellow answerlineall(colour) trylinesall(colour) scorelinesall(purple) elif count % 8 == 6: #AQUA colour = aqua answerlineall(colour) trylinesall(colour) scorelinesall(white) elif count % 8 == 7: #PURPLE colour = purple answerlineall(colour) trylinesall(colour) scorelinesall(purple) self.usleep(10 * 1000) #whole game loop starts here while True: #clear he matrix here self.matrix.Clear() self.matrix.brightness = max_brightness #randomize answer count = 0 #initial random sequence turnoff = 5 while count < 10: anspos = 0 while anspos < turnoff: colour = colours[random.randint(0,7)] answerline(anspos,colour) anspos = anspos + 1 anspos = 0 self.usleep(100 * 1000) count = count + 1 count = 0 #pick an answer and turn off lamps while turnoff > 0: while count < 10: anspos = 0 while anspos < turnoff: colour = colours[random.randint(0,7)] answerline(anspos,colour) anspos = anspos + 1 anspos = 0 self.usleep(100 * 1000) count = count + 1 count = 0 turnoff = turnoff - 1 answer[turnoff] = colours[random.randint(0,7)] answerline(turnoff,off) #for debug print(answer[turnoff]) #start of main game loop here pos1 = 0 pos2 = 0 pos3 = 0 pos4 = 0 pos5 = 0 playertry = 0 while playertry < 12: #pulse score line press = False count = 0 while count < 6: #wait for button press for pin in button: if GPIO.input(pin) == 0: press = True count = 6 pulsescoreline(playertry) self.usleep(60 * 1000) count = count + 1 #Clear score line for sl in range(5): scoreline(playertry,sl,off) #select your answer #set the lamps to the prev answer or all red if first try tryline(playertry,0,colours[pos1]) tryline(playertry,1,colours[pos2]) tryline(playertry,2,colours[pos3]) tryline(playertry,3,colours[pos4]) tryline(playertry,4,colours[pos5]) self.usleep(600 * 1000) #wait for button push pressed = 0 while pressed != 7: #wait for button press for pin in button: if GPIO.input(pin) == 0: pressed = pin print(pin) if pressed == 8: #button 1 pos1 = pos1 + 1 if pos1 > 7: pos1 = 0 tryline(playertry,0,colours[pos1]) while GPIO.input(8) == 0: self.usleep(10 * 1000) pressed = 0 elif pressed == 11: #button 2 pos2 = pos2 + 1 if pos2 > 7: pos2 = 0 tryline(playertry,1,colours[pos2]) while GPIO.input(11) == 0: self.usleep(10 * 1000) pressed = 0 elif pressed == 25: #button 3 pos3 = pos3 + 1 if pos3 > 7: pos3 = 0 tryline(playertry,2,colours[pos3]) while GPIO.input(25) == 0: self.usleep(10 * 1000) pressed = 0 elif pressed == 9: #button 4 pos4 = pos4 + 1 if pos4 > 7: pos4 = 0 tryline(playertry,3,colours[pos4]) while GPIO.input(9) == 0: self.usleep(10 * 1000) pressed = 0 elif pressed == 10: #button 5 pos5 = pos5 + 1 if pos5 > 7: pos5 = 0 tryline(playertry,4,colours[pos5]) while GPIO.input(10) == 0: self.usleep(10 * 1000) pressed = 0 self.usleep(10 * 1000) #score the answer that was input guess = [colours[pos1],colours[pos2],colours[pos3],colours[pos4],colours[pos5]] score = [off,off,off,off,off] tmpguess = guess[:] tmpanswer = answer[:] scorepos = 4 #score black (purple) pegs for guesspos, guesscol in enumerate(tmpguess): if guesscol == tmpanswer[guesspos]: print("Black Peg") score[scorepos] = purple tmpguess[guesspos] = off tmpanswer[guesspos] = off scorepos = scorepos - 1 #score white pegs for guesspos, guesscol in enumerate(tmpguess): for answerpos, answercol in enumerate(tmpanswer): if answercol == off or guesscol == off: continue if answercol == guesscol: print("White Peg") score[scorepos] = white tmpguess[guesspos] = off tmpanswer[answerpos] = off scorepos = scorepos - 1 break print("Score",score) #light up the score row for sl in range(5): scoreline(playertry,sl,score[sl]) #check if won if score == [purple, purple, purple, purple, purple]: playertry = 12 print("WINNER") playertry = playertry + 1 #all trys used - or guessed correct - end of game print("END") count = 0 while count < 6: for al in range(5): answerline(al,off) #answerline(1,off) #answerline(2,off) #answerline(3,off) #answerline(4,off) self.usleep(100 * 1000) for al in range(5): answerline(al,answer[al]) #answerline(1,answer[1]) #answerline(2,answer[2]) #answerline(3,answer[3]) #answerline(4,answer[4]) self.usleep(100 * 1000) count = count + 1 press = False while press == False: #wait till a button is pressed for pin in button: if GPIO.input(pin) == 0: press = True self.usleep(100 * 1000) # Main function if __name__ == "__main__": #setup GPIO GPIO.setmode(GPIO.BCM) button = [10, 9, 25, 11, 8, 7] for pin in button: GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) master_mind = MasterMind() if (not master_mind.process()): master_mind.print_help()
You will also need the samplebase.py file in the same directory:
import argparse import time import sys import os sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..')) from rgbmatrix import RGBMatrix, RGBMatrixOptions class SampleBase(object): def __init__(self, *args, **kwargs): self.parser = argparse.ArgumentParser() self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int) self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int) self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int) self.parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int) self.parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int) self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int) self.parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm" , choices=['regular', 'regular-pi1', 'adafruit-hat', 'adafruit-hat-pwm'], type=str) self.parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int) self.parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int) self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel") self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=1, type=int) self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation") self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str) self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str) self.parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels; 2=row direct; 3=ABC-addressed panels; 4 = ABC Shift + DE direct", default=0, type=int, choices=[0,1,2,3,4]) self.parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven... (Default: 0)", default=0, type=int) self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str) self.parser.add_argument("--led-no-drop-privs", dest="drop_privileges", help="Don't drop privileges from 'root' after initializing the hardware.", action='store_false') self.parser.set_defaults(drop_privileges=True) def usleep(self, value): time.sleep(value / 1000000.0) def run(self): print("Running") def process(self): self.args = self.parser.parse_args() options = RGBMatrixOptions() if self.args.led_gpio_mapping != None: options.hardware_mapping = self.args.led_gpio_mapping options.rows = self.args.led_rows options.cols = self.args.led_cols options.chain_length = self.args.led_chain options.parallel = self.args.led_parallel options.row_address_type = self.args.led_row_addr_type options.multiplexing = self.args.led_multiplexing options.pwm_bits = self.args.led_pwm_bits options.brightness = self.args.led_brightness options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds options.led_rgb_sequence = self.args.led_rgb_sequence options.pixel_mapper_config = self.args.led_pixel_mapper options.panel_type = self.args.led_panel_type if self.args.led_show_refresh: options.show_refresh_rate = 1 if self.args.led_slowdown_gpio != None: options.gpio_slowdown = self.args.led_slowdown_gpio if self.args.led_no_hardware_pulse: options.disable_hardware_pulsing = True if not self.args.drop_privileges: options.drop_privileges=False self.matrix = RGBMatrix(options = options) try: # Start loop print("Press CTRL-C to stop sample") self.run() except KeyboardInterrupt: print("Exiting\n") sys.exit(0) return True
(CTRL+C to quit the program)
To get the program to autorun when the Pi is powered on edit the rc.local file using sudo nano /etc/rc.local
Tutorial here: https://learn.sparkfun.com/tutorials/how-to-run-a-raspberry-pi-program-on-startup/method-1-rclocal
Insert the command:
sudo bash -c '/usr/bin/python3 /home/pi/mastermind/mm-main-game.py -r 16 -b 100 > /home/pi/mastermind/mm-main-game.log 2>&1' &
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. # Print the IP address _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi sudo bash -c '/usr/bin/python3 /home/pi/mastermind/mm-main-game.py -r 16 -b 100 > /home/pi/mastermind/mm-main-game.log 2>&1' & exit 0
Button Connections
Cut 4 Pin and 3 Pin sections of a header and solder them to the HAT on Pins 20,22,24 and 26 (outer side); and Pins 19,21 and 23 (Inner side) – see photo: (please forgive my horrible soldering skills)
Also see the pinout of all of the connections.
3D Printing
I chose the colours as this kind-of matches the 70’s retro theme :)
I would recommend a quality PLA like Prima Select – as it’s strong and makes accurate prints. Last thing you want is the 20 hour print cracking when pressing in the light covers…
Print the Case top and bottom in brown PLA – these are long prints!
Print the button caps and bracket in red PLA
Print the light covers in transparent PLA – you’ll need 65 large and 60 small
Press fit the light covers into the top cover – these just snaped in for me but you might need to either glue them if they are loose or use a round file to make them fit.
Press fit the button caps and test fit to the top cover, might need a bit of filing to make the move smoothly.
Test fit the matrix into the top cover, the switch bracket and switches and make sure everything fits together correctly.
Button Wiring
Once you are happy, superglue the buttons onto the bracket – making sure you get the alignment just right through the top cover.
Due to the constraints of the size of the matrix and the print bed size, there is very little room for the buttons and the wiring; so I used stiff wire to connect up all the ground pins of the buttons on one side, and soldered short lengths to the other side so I could connect the dupont wires underneath.
Taking careful note of which wire is which, feed the wires through the holes and carefully seat the buttons down into the case.
Connect the wires to the pins soldered earlier on to the board, making sure you get the correct pin connected to the board.
Pin Pi Pins Notes
19 GPIO 10 Button 1
20 Ground Button Ground
21 GPIO 9 Button 2
22 GPIO 25 Button 3
23 GPIO 11 Button 4
24 GPIO 8 Button 5
26 GPIO 7 Set Button
Power Connector
Solder the short male barrel jack connector to the external barrel jack connector. (both of these parts were bought on ebay very cheaply)
Then connect it to the internal power connector on the Matrix hat board. This could also be achieved with a direct soldered connection wire, but I found it easier this way.
Final Construction
All that is really left to do now is to fit the top cover and glue the case shut. I probably should have designed the case for screw fittings but I hadn’t thought about that when I designed the case.
Playing the Game!
Power on the game by connecting the 5v power supply.
The system will take about 1 minute to boot, and once ready, the front panel will pulse all the lights to indicate it is ready to go.
A copy of the original games instructions is attached.
Press any button and the game will pick a random code and hide the result.
The game will then indicate the first row and light up the first set of code lights.
Press a corresponding button to change the colour of the code, there are eight colours; Red, Blue, Green, White, Orange, Yellow, Aqua, & Purple. The buttons will cycle through the colours on each press.
Once you are happy press the right button to enter your guess, the game will the score you as follows:
A purple light indicates there is a code of the correct colour and location – the position of the score light does NOT indicate that this is the correct location!
A White light indicates there is a code of the correct colour but in the wrong location – again the position of the score light does NOT indicate which colour is correct!
Once the score is indicated the game will light up the next row to allow you to modify you guess and try again.
If you guess correct or you run out of guesses the secret code will be revealed on the top row.
Good luck Master Mind!