A link in the comments helped me associate these with the correct numbers. On that note, this StackOverflow answer by jose helped a lot to use pygame.midi.
I wanted to make a Simon that was simple but true to the original. Listed below are some features added to that end.
Levels of Difficulty
I have levels of difficulty that are considered a win when a sequence of a given length is repeated back. The levels 1-4 correspond to the Wiki's list of 8, 14, 20, and 31. For historical value (i.e., I used it to test) there is a level 0 that asks for a user to play back a note. There is also an unending level. Selection of difficulty is the first screen the user sees.Sequences Gradually Quicken
I noticed in LuckyPennyShop.com's YouTube video Vintage Electronic Simon Game 1978 Milton Bradley Toys that the sequence not only got longer but played faster. This not only makes the game more difficult, but it also brings you to the new information faster. For this I used a variation of
where 0 < c < 1 is a non-negative scaling constant and ell is the length of the list. (c = 1 made sequences play too quickly for long sequences.) A benefit to this function is that it decreases, but levels out asymptotically to zero. We can add a minimum m not to go faster than.
Correct Tile Blinks On Miss
I noticed that the ending tile in the video above blinks several times when the sequence is ended—correctly, if I followed the video right. I resort to an end screen. However, for incorrect tiles the tile that should have been pressed blinks 4 times. (I don't know if this happens in the original.)
Images of Gameplay
A board of unlit tiles looks like this:
Compare to this one, where the red button is lit:
When the game is over this end screen comes up:
Clicking Replay takes the user back to the difficulty screen and Quit exits. There are several places where I call the end() functions. An improvement might be adding threads, one that keeps track of when the window's "x" button is clicked and another for inside the game.
Here's the code:
'''Simple Simon''' import math, time, pygame, pygame.midi, random pygame.midi.init() pygame.font.init() audio = pygame.midi.Output(0) audio.set_instrument(4,1) # midi chart: http://www.electronics.dit.ie/staff/tscarff/Music_ # technology/midi/midi_note_numbers_for_octaves.htm BLACK = (255,255,255) colors = [(0,150,0),(150,0,0),(150,150,0),(0,0,150)] #g,r,y ,b sounds = [52,57,61,64] #e,a,c#,e_hi hardness = {'0':1,'1':8, '2':14, '3':20, '4':31, '∞': -1} size = 200 time_scale = .1 min_pause = .1 n = math.ceil(math.sqrt(len(colors))) win_size = n*size mid = round(n*size/2) window = pygame.display.set_mode( (win_size, win_size) ) pygame.display.set_caption("Simple Simon") class Pane: def __init__(self,text,pos,font_size=40,color=BLACK,fun=None): self.fun = fun self.text = text font = pygame.font.SysFont('Arial', font_size) surf = font.render(text, True, color) self.rect = surf.get_rect() self.rect.center = pos window.blit(surf, self.rect.topleft) class Button: def __init__(self,i): self.x = size * (i%n) self.y = size * (i//n) self.color = colors[i] self.sound = sounds[i] self.rect = pygame.Rect( (self.x,self.y), (size,size) ) self.draw() def draw(self, lit=False, wait=0): bright = tuple(255 if i else 0 for i in self.color) color = bright if lit else self.color if lit: audio.note_on(self.sound,127,1) else: audio.note_off(self.sound,127,1) pygame.draw.rect(window,color,self.rect) pygame.display.update() time.sleep(wait) def end(): audio.close() pygame.midi.quit() pygame.quit() quit() def choose_mode(ts=60,ms=40): window.fill( (0,0,0) ) while True: modes = enumerate(sorted(hardness.keys())) title = Pane('Select hardness', (mid,ts/2), ts) diffs = [Pane(x,(mid,ms*i+1.5*ts),ms,) for i,x in modes] pygame.display.update() for event in pygame.event.get(): if event.type == pygame.QUIT: end() if event.type == pygame.MOUSEBUTTONDOWN: for diff in diffs: if diff.rect.collidepoint(event.pos): return diff.text def simon(): player,wrong = False,False sequence = [] correct = 0 mode = choose_mode() tiles = [Button(i) for i in range(len(colors))] while True: # player's turn if player: for event in pygame.event.get(): if event.type == pygame.QUIT: end() if event.type == pygame.MOUSEBUTTONDOWN: for tile in tiles: if tile.rect.collidepoint(event.pos): tile.draw(lit=True) if tile == sequence[correct]: correct += 1 else: wrong = True # letting go of mouse button redraws all tiles elif event.type == pygame.MOUSEBUTTONUP: [tile.draw() for tile in tiles] # correct tile blinks on miss if wrong: right = sequence[correct] for i in range(4): right.draw(lit=True,wait=.1) right.draw(wait=.1) end_screen(len(sequence),mode,'Lost') # player used correct sequence elif correct == len(sequence): correct = 0 player = False # computer's turn else: chain = len(sequence) pause = math.exp(-time_scale*chain) # player wins, if not in infinite mode if chain == hardness[mode]: end_screen(chain, mode) # pause before showing next sequence time.sleep(min_pause + pause) sequence.append(random.choice(tiles)) for tile in sequence: tile.draw(lit=True,wait=pause) tile.draw(wait=min_pause*pause) # player can quit mid-sequence for event in pygame.event.get(): if event.type == pygame.QUIT: end() player = True pygame.display.update() def end_screen(streak, mode, result='Won', fsize=40): funs = [None, None, simon, end] most = str(hardness[mode]) if mode.isalnum() else '∞' texts = ['You %s!' % result, 'Sequence: %d/%s' % (streak,most), 'Replay', 'Quit'] data = enumerate(zip(texts,funs)) panes = [Pane(x[0],(mid,(i+1)*1.5*fsize),fsize,fun=x[1]) for i,x in data] pygame.display.update() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: end() if event.type == pygame.MOUSEBUTTONDOWN: for pane in panes: if pane.rect.collidepoint(event.pos): if pane.fun: pane.fun() simon()
No comments:
Post a Comment