28/05/09

Mastermind, a little more refined...

J'avais posté ici un petit jeu de Mastermind, un furieux premier jet né d'un film trop peu intéressant à la TV et d'une envie de coder "quelque chose". D'une manière ou d'une autre ça valait la peine de faire quelque chose d'un peu plus peaufiné un jour.

Voici qui est un peu mieux. Toujours une interface textuelle sans fioritures, mais le jeu est paramétrable (voir le menu options) et offre le mode de jeu classique et le mode de jeu facile qu'implémentait ma version "quick and dirty".

Au niveau du code c'est du plus propre sans pour autant être de l'objet-pour-faire-de-l'objet. Hack away, have fun!

Ah oui, pour que ce soit un peu plus lisible il n'y a pas de contrôle aux entrées clavier. Si on tape n'importe quoi ça plante de façon insultante, soyez prévenu :)


#! /usr/bin/env python

# Game of Mastermind, with some niceties
# Jean Karim Bockstael - jkb@jkbockstael.be


# Import
from random import randint

# Gameplay constants
CODE_LENGTH = 4 # Length of the code (1-16)
CODE_COLORS = 10 # Number of different colors in the code (2-10)
MAX_GUESSES = 5 # Max number of guesses to find the correct code (1-50)
SHOW_MENU = True # Show main menu before playing a first game
EASY_MODE = True # Braindead mode

# String constants
STR_GUESS_PROMPT = 'Guess'
STR_WHITE_PEG = 'o'
STR_BLACK_PEG = 'x'
STR_EMPTY_PEG = '_'
STR_OUTCOME_WIN = 'Won!'
STR_OUTCOME_LOSS = 'Lost!'
STR_OUTCOME_REVEAL = 'The secret code was: '
STR_HELP = """
{codelength} digits long code, each between 0 and {codecolors} included, you have a maximum of {maxguesses} guesses to find it.
{whitepeg} : right digit, right position
{blackpeg} : right digit, wrong position
{emptypeg} : wrong digit
"""
STR_OPT_CODELENGTH = 'Number of digits in code (min 1 max 16): '
STR_OPT_CODECOLORS = 'Number of possible digits in code (min 2 max 10): '
STR_OPT_MAXGUESSES = 'Maximum number of guesses before giving up (min 1 max 50): '
STR_OPT_SHOWMENU = 'Show main menu on program startup? (False/True): '
STR_OPT_EASYMODE = 'Play in easy mode? (False/True): '
STR_MENU_MAIN = """
----------------
-- Mastermind --
----------------
A)bout
O)ptions
P)lay
Q)uit
"""
STR_MENU_MAINPROMPT = "Choice: "
STR_MENU_OPTIONS = """
------------------
-- Game Options --
------------------
"""
STR_ABOUT = """
----------------------
-- About Mastermind --

A simple and time-honored game, brought to your console using Python.

2009 - Jean Karim Bockstael - jkb@virus1984.com
"""


# And now, let the fun begin!
# (__)
# ( @@
# /\_| MOOH!


# Create a code of set length and number of colors, as a list of ints
def set_secret_code():
tmp_code = []
for i in range(0, CODE_LENGTH):
tmp_code.append(randint(0, CODE_COLORS - 1))
return tmp_code


# Read a code from the user
def get_guess_code():
tmp_guess = []
padding_spaces = ''
if (len(STR_GUESS_PROMPT) < CODE_LENGTH):
padding_spaces = " " * (CODE_LENGTH - len(STR_GUESS_PROMPT))
guess_prompt = STR_GUESS_PROMPT + padding_spaces + " : "
str_guess = raw_input(guess_prompt)
for i in range(0, CODE_LENGTH):
tmp_guess.append(int(str_guess[i]))
return tmp_guess


# Print a help message
def print_help():
print STR_HELP.format(codelength=CODE_LENGTH, codecolors=(CODE_COLORS-1), \
maxguesses=MAX_GUESSES, whitepeg=STR_WHITE_PEG, blackpeg=STR_BLACK_PEG, \
emptypeg=STR_EMPTY_PEG)


# Returns a code formatted for printing
def format_code(code):
formatted_code = ''
for i in range(0, CODE_LENGTH):
formatted_code += str(code[i])
return formatted_code


# Print a hint based on the player's guess
def print_hint(secret_code, guess_code):
hint = ''
if (EASY_MODE):
for i in range(0, CODE_LENGTH):
if (guess_code[i] == secret_code[i]):
hint += STR_WHITE_PEG
elif (guess_code[i] in secret_code):
hint += STR_BLACK_PEG
else:
hint += STR_EMPTY_PEG
else: # Badass mode
white_pegs = 0
black_pegs = 0
dupes = [False] * CODE_LENGTH
for i in range(0, CODE_LENGTH):
if (guess_code[i] == secret_code[i]):
white_pegs += 1
dupes[i] = True
else:
for j in range(0, CODE_LENGTH):
if (secret_code[j] == guess_code[i] and not dupes[j]):
black_pegs += 1
if (white_pegs != 0):
hint += str(white_pegs) + ' ' + STR_WHITE_PEG + ' '
if (black_pegs != 0):
hint += str(black_pegs) + ' ' + STR_BLACK_PEG
if (white_pegs == 0 and black_pegs == 0):
hint += STR_EMPTY_PEG
padding_spaces = ''
if (CODE_LENGTH < len(STR_GUESS_PROMPT)):
padding_spaces = " " * (len(STR_GUESS_PROMPT) - CODE_LENGTH)
print format_code(guess_code), padding_spaces + ':', hint


# Print the game outcome
def print_outcome(code_found, secret_code):
if (code_found):
print STR_OUTCOME_WIN
else:
print STR_OUTCOME_LOSS
print STR_OUTCOME_REVEAL, format_code(secret_code)


# Play a game of Mastermind
def play_game():
print_help()
secret_code = set_secret_code()
guess_num = 0
code_found = False
while (guess_num < MAX_GUESSES):
guess_code = get_guess_code()
guess_num += 1
code_found = (guess_code == secret_code)
if (code_found):
break # Avoid printing a hint if the code is found
print_hint(secret_code, guess_code)
print_outcome(code_found, secret_code)


# Options menu
def menu_options():
global CODE_LENGTH
global CODE_COLORS
global MAX_GUESSES
global SHOW_MENU
global EASY_MODE
print STR_MENU_OPTIONS
print STR_OPT_CODELENGTH
CODE_LENGTH = int(input("[" + str(CODE_LENGTH) + "] "))
print STR_OPT_CODECOLORS
CODE_COLORS = int(input("[" + str(CODE_COLORS) + "] "))
print STR_OPT_MAXGUESSES
MAX_GUESSES = int(input("[" + str(MAX_GUESSES) + "] "))
print STR_OPT_SHOWMENU
SHOW_MENU = input("[" + str(SHOW_MENU) + "] ")
print STR_OPT_EASYMODE
EASY_MODE = input("[" + str(EASY_MODE) + "] ")


# Main menu
def menu_main():
menu_main_choices = { "A" : show_about,
"O" : menu_options,
"P" : play_game,
"Q" : None }
usr_choice = ''
while (True):
print STR_MENU_MAIN
while (not usr_choice in menu_main_choices):
usr_choice = raw_input(STR_MENU_MAINPROMPT)
if (usr_choice == 'Q'):
return None
else:
menu_main_choices[usr_choice]()
usr_choice = ''


# Show "about" screen
def show_about():
print STR_ABOUT
raw_input()


# Main
if (SHOW_MENU):
menu_main()
else:
play_game()

# End



...la suite par ici...

14/05/09

"I know the pieces fit"

Houlà ça fait un furieux bail que je n'ai rien posté ici, on va finir par croire que j'ai vraiment une vie hyperactive de jeune cadre dynamique, ou que je me suis fait emporter par H1N1, au choix. Il est temps d'y remédier! Et histoire de varier les plaisirs je vais parler musique et mathématiques (légères, ne pas stresser), plus particulièrement de Tool.

Il y a quelques jours a surgi sur Reddit un lien vers une vidéo d'un fan de Tool montrant les curiosités mathématiques de leur morceau "Lateralus" extrait de l'album du même nom. La vidéo en question ne date pas d'hier et avait même été diggée quelques fois, mais le morceau est une pièce d'exception et il est toujours intéressant d'en découvrir les particularités.

Tool est un de ces groupes un peu monstrueux parce que chaque élément est une montagne de talent et de créativité à lui seul, mais qu'en plus une fois mis ensemble une curieuse alchimie fait que le groupe est supérieur à la somme de ses membres. En presque vingt ans d'activité ils ont sorti 4 albums (en 1993, 1996, 2001 et 2006), chacun étant clairement réfléchi, mûri et peaufiné pour atteindre un degré de perfection presque monstrueux. Leur chef-d'oeuvre incontesté reste à ce jour l'album Lateralus, sorti en 2001, catapulé en première place des ventes US dès sa sortie, et vainqueur d'un Grammy Award, et album de l'année selon Kerrang!, rien que ça...

La chanson Lateralus, 9 minutes et 24 secondes de bonheur, se fait un peu plus remarquer que le reste. La chanson parle de la quête de connaissance, de la libération de l'esprit sur le corps (on entendait déjà "This body holding me reminds me of my own mortality" sur Parabola) et culmine en la répétition de "Spiral out, keep going...". Pas grand-chose, mais si on est attentif aux pauses qui sont mises dans le chant du premier couplets, on observe ceci:

Black
then
white are
all I see
in my infancy.
Red and yellow then came to be,
reaching out to me.
Lets me see

As below, so above and beyond, I imagine.
Drawn beyond the lines of reason.
Push the envelope.
Watch it bend.

Soit en syllabes: 1, 1, 2, 3, 5, 8, 5, 3 puis 13, 8, 5, 3.

Après le refrain on reprend le même couplet mais avec un morceau qui manquait, et qui rend la structure nettement plus évidente:

Black
then
white are
all I see
in my infancy.
Red and yellow then came to be,
reaching out to me.
Lets me see

There is
so
much
more and
beckons me
to look through to these
infinite possibilities.

As below, so above and beyond, I imagine.
Drawn beyond the lines of reason.
Push the envelope.
Watch it bend.

Soit 1, 1, 2, 3, 5, 8, 5, 3, 2, 1, 1, 2, 3, 5, 8, 13, 8, 5, 3. Autrement dit, la suite de Fibonnaci dans le sens croissant puis décroissant sur ses premiers termes, deux fois. Ce qui, au vu du thème de la chanson et de la mentalité de son auteur, n'est absolument pas à attribuer au hasard. Un autre détail amusant? Le chant commence après 97 secondes de musique, soit après 1,618 minutes, 1,618 est le nombre d'or.

Waw.

Le refrain est sur une base rythmique de 9/8, puis 8/8, puis 7/8. 987 est un des termes de la suite de Fibonacci, mais le batteur Danny Carey a démenti que ce soit intentionnel, bien qu'il soit fasciné par les questions de numérologies et des géométries et que cela se ressente très nettement dans la complexité de son jeu.

L'album comporte 13 pistes, le terme de la série de Fibonnaci le plus grand du jeu rythmique de la plage titulaire. Une rumeur voulait que les pistes de l'album ne soient pas dans le "bon" ordre, et à cette question un fan a répondu en proposant un ordre de lecture différent et étonnament agréable. Son idée est que Parabol est un prélude à Parabola et sont respectivement 6ème et 7èmes pistes, donc au milieu du disque. Son arrangement commence par 6 et 7, puis continue par paires de nombres dont la somme vaut 13, le premier étant d'abord décroissant, puis croissant. Soit 6 et 7, 5 et 8, 4 et 9, 13, 1 et 12, 2 et 11, 3 et 10. L'ordre 6, 7, 5, 8, 4, 9, 13, 1, 12, 2, 11, 3, 10, baptisé The Holy Gift par son auteur, est effectivement troublant tellement il "coule" bien, mais il présente un petit hic: Tool joue toujours les morceaux Disposition, Reflection, et Triad ensemble et dans cet ordre, qui est l'ordre dans lequel ils apparaissent sur Lateralus, aux positions 10, 11, et 12. The Holy Gift casse ce triptique abruptement, ce qui peut laisser supposer que s'il existe un ordre parfait ce n'est pas celui-ci.

C'est tout de même troublant.

...la suite par ici...