-
-
Save scottmries/ac5298969a313f478ed3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding=utf-8 | |
from sys import argv | |
import random, time | |
def bold(s): | |
return '\033[1m'+s+'\033[0m' | |
def italic(s): | |
return " ** "+bold(s) | |
def rand_color(s): | |
return " "*5+'{'+bold(s)+'}' | |
def clear(): | |
print(chr(27) + "[2J") | |
def help(): | |
clear() | |
print bold("Nim"), "is a simple game to learn." | |
print "\r\n" | |
print "Each player takes at least one piece, a pipe | in this version, from one row." | |
print "In the standard version, called misère, the object is to make the opponent take the last piece." | |
print "(In non-misère play, the last player to take a piece wins.)" | |
print "Be warned that Nim is a solved game; even with the advantage of going first, it is extremely difficult to beat the computer." | |
print """Nim appears in the 1961 Alain Resnais film "Last Year at Marienbad" ("L'Année dernière à Marienbad"), dialogue from which decorates this game.""" | |
print "(Partial source: http://plambeck.org/archives/Marienbad.html.)" | |
print "\r\n" | |
print "To play a custom game, use 'custom' (no quotes) as the first argument." | |
print "The second argument is a misère flag (True/False), and the third is to take the first turn (True/False)." | |
print "The final argument, which is optional, is a list of row lengths for a custom board." | |
print "The standard board is 1 3 5 7, but a list of numbers of any length may be used." | |
print "\r\n" | |
class Board(object): | |
def __init__(self,rows): | |
self.rows = rows | |
def draw(self): | |
for e,i in enumerate(self.rows): | |
print "Row %s:" % str(e+1),bold("| ")*int(i) | |
def update(self,row,draws): | |
self.rows[row] -= draws | |
def nim_sum(self): | |
nim_sum = 0 | |
for r in self.rows: | |
nim_sum ^= r | |
return nim_sum | |
class Player(object): | |
def __init__(self,name,dialogue,win_string): | |
self.name = name | |
def win(self): | |
clear() | |
raw_input(bold(self.win_string)) | |
class User(Player): | |
def __init__(self): | |
self.name = "user" | |
self.win_string = "Congratulations, you have won." | |
self.walk = False | |
self.chosen_dialogue = list() | |
def take_turn(self,board): | |
row = self.prompt_row(board,"Select a row.") | |
draws = self.prompt_draw(board,row,"How many do you take from row %s?" % str(int(row)+1)) | |
choose = random.randint(1,100) | |
if choose>83 and not self.walk: | |
walk = Speech("italic","And once again I walked on alone,", 2) | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors," | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms," | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades," | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries." | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries.\r\n ** I crossed these same thresholds," | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries.\r\n ** I crossed these same thresholds,\r\n ** picking my way as if it at random" | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries.\r\n ** I crossed these same thresholds,\r\n ** picking my way as if it at random\r\n ** through a maze of identical paths." | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries.\r\n ** I crossed these same thresholds,\r\n ** picking my way as if it at random\r\n ** through a maze of identical paths.\r\n\r\n ** And once again," | |
walk.display() | |
walk.dialogue = "And once again I walked on alone,\r\n ** down these same corridors,\r\n ** through these same empty rooms,\r\n ** I passed these same colonnades,\r\n ** these same windowless galleries.\r\n ** I crossed these same thresholds,\r\n ** picking my way as if it at random\r\n ** through a maze of identical paths.\r\n\r\n ** And once again,\r\n ** everything was deserted in this vast hotel." | |
walk.display() | |
self.walk = True | |
elif choose<52: | |
while True: | |
choose = random.randint(0,12) | |
if choose not in self.chosen_dialogue: | |
self.chosen_dialogue.append(choose) | |
break | |
one_lines = ("I think this game's silly.","There must be rules.") | |
if choose < len(one_lines)-1: | |
rand_speech = Speech("random", one_lines[choose],2) | |
rand_speech.display() | |
elif choose==3: | |
rand_speech = Speech("random", "I remember how Frank used to play this last year . . .",2) | |
rand_speech.display() | |
rand_speech = Speech("random", "I remember how Frank used to play this last year . . . }\r\n {Yes, yes he did, I'm sure of it.",2) | |
rand_speech.display() | |
else: | |
rand_speech = Speech("random", "There's a trick you have to know.",2) | |
rand_speech.display() | |
tricks = ("What you have to do is take the complement of seven each time.","All you have to do is take an uneven number.","It's the one who goes first who loses.","It's the one who starts who wins.","You have to take an even number.","The lowest whole uneven number.","It's a logarithmic series.","You have to pick a different row each time.","Divided by three.","Seven times seven forty-nine.") | |
choose_tricks = choose - 4 | |
rand_speech = Speech("random",tricks[choose_tricks],2) | |
rand_speech.display() | |
if choose_tricks == 0: | |
rand_speech = Speech("random","In which row?",2) | |
rand_speech.display() | |
board.update(row,draws) | |
def prompt_row(self,board,prompt): | |
clear() | |
board.draw() | |
row = raw_input(bold(prompt)) | |
if row == '': | |
return self.prompt_row(board,"Please choose a row with at least one piece.") | |
else: | |
row = str(int(row)-1) | |
valid_inputs = [str(e) for e,r in enumerate(board.rows) if r > 0] | |
if row in valid_inputs: | |
return int(row) | |
else: | |
return self.prompt_row(board,"Please choose a row with at least one piece.") | |
def prompt_draw(self, board, row, prompt): | |
clear() | |
board.draw() | |
if board.rows[row] == 1: | |
raw_input(bold("You take 1 from row %s." % (row+1))) | |
return 1 | |
else: | |
draws = raw_input(bold(prompt)) | |
valid_inputs = [str(x) for x in range(1,board.rows[row]+1)] | |
if draws in valid_inputs: | |
return int(draws) | |
else: | |
if board.rows[row] > 1: | |
return self.prompt_draw(board, row, "Please input a number between 1 and %s." % board.rows[row]) | |
else: | |
return self.prompt_draw(board, row, "You may only take 1 row that row.") | |
class Computer(Player): | |
def __init__(self): | |
self.name = "comp" | |
self.win_string = "Sorry, you lost." | |
self.extra = False | |
def take_turn(self,board): | |
if sum(board.rows)==1: | |
row, draws = board.rows.index(1),1 | |
else: | |
X = board.nim_sum() | |
correct_moves = list() | |
if X != 0: | |
for e, r in enumerate(board.rows): | |
# make an independent copy of the board | |
test_board = Board(list(board.rows)) | |
for p in range(1,r+1): | |
test_board.rows[e] = r-p | |
if test_board.nim_sum() == 0: | |
correct_moves.append([e,p]) | |
i = 0 | |
if game.misere == True: | |
for m in correct_moves: | |
test_board = Board(list(board.rows)) | |
test_board.rows[m[0]] -= m[1] | |
if all(r<2 for r in test_board.rows): | |
correct_moves.remove(m) | |
if len(correct_moves)<1: | |
for e, r in enumerate(board.rows): | |
test_board = Board(list(board.rows)) | |
for p in range(1,r+1): | |
test_board.rows[e] = r-p | |
ones = test_board.rows.count(1) | |
zeroes = test_board.rows.count(0) | |
if ones%2==1 and ones + zeroes == len(test_board.rows): | |
correct_moves.append([e,p]) | |
if len(correct_moves) > 1: | |
turn = random.choice(correct_moves) | |
else: | |
turn = correct_moves[0] | |
row, draws = turn[0],turn[1] | |
else: | |
while True: | |
row = random.randint(0,len(board.rows)-1) | |
# print "row %s" % row | |
if not self.extra: | |
ext = Speech("bold","Extraordinary.",2) | |
ext.display() | |
self.win_string = "Actually, it wasn't so extraordinary after all." | |
self.extra = True | |
clear() | |
board.draw() | |
if board.rows[row] > 0: | |
break | |
row,draws = row,1 | |
raw_input(bold("I will take %s from row %s." % (draws,row+1))) | |
board.update(row,draws) | |
class Speech(object): | |
def __init__(self,context,dialogue,delay): | |
self.dialogue = dialogue | |
self.context = context | |
self.delay = delay | |
def display(self): | |
if self.context == "bold": | |
self.dialogue = " "*5 + bold(self.dialogue) | |
elif self.context == "italic": | |
self.dialogue = italic(self.dialogue) | |
elif self.context == "random": | |
self.dialogue = rand_color(self.dialogue) | |
elif self.context == "title": | |
self.dialogue = bold(self.dialogue) | |
clear() | |
print self.dialogue | |
time.sleep(self.delay) | |
class Game(object): | |
def __init__(self,custom,misere,first_turn,board,restart): | |
self.restart = restart | |
self.custom = custom | |
self.misere = misere | |
self.first_turn = first_turn | |
self.user = User() | |
self.comp = Computer() | |
self.board = Board(board) | |
self.start_board = Board(board) | |
if self.first_turn == True: | |
self.player = self.user | |
else: | |
self.player = self.comp | |
def play(self): | |
clear() | |
self.board.draw() | |
self.player.take_turn(self.board) | |
self.end_test() | |
def end_test(self): | |
end = all(r == 0 for r in self.board.rows) | |
if end: | |
if self.player.name == "user": | |
if self.misere == True: | |
self.comp.win() | |
else: | |
self.user.win() | |
else: | |
if self.misere == True: | |
self.user.win() | |
else: | |
self.comp.win() | |
self.restart_prompt() | |
else: | |
if self.player == self.user: | |
self.player = self.comp | |
else: | |
self.player = self.user | |
self.play() | |
def restart_prompt(self): | |
clear() | |
repeat = raw_input(bold("Would you like to play again?")+" (y/n)") | |
if repeat == "y" or repeat == "n": | |
if repeat == "y": | |
print "start board", self.start_board.rows | |
if self.custom: | |
game = Game(self.custom,self.misere,self.first_turn,[int(r) for r in argv[4:]],True) | |
else: | |
game = Game(self.custom,self.misere,self.first_turn,[1,3,5,7],True) | |
game.start() | |
else: | |
quit() | |
else: | |
return self.restart_prompt() | |
def start(self): | |
if not self.restart: | |
intro = Speech("title","Nim",2) | |
intro.display() | |
intro.delay = 1 | |
if board == [1,3,5,7]: | |
intro = Speech("italic","one, two, three, four, five, six, seven . . .",2) | |
intro.display() | |
intro.dialogue = "one, two, three, four, five . . ." | |
intro.display() | |
intro.dialogue = "one, two, three . . ." | |
intro.display() | |
intro.dialogue = "one . . ." | |
intro.display() | |
if self.first_turn == True: | |
if not self.restart: | |
intro = Speech("bold","What if you go first?",2) | |
intro.display() | |
else: | |
if not self.restart: | |
intro = Speech("italic","I'd like you to begin.",2) | |
intro.display() | |
intro = Speech("bold","With pleasure . . .",2) | |
intro.display() | |
intro = Speech("italic","(He had started the whole thing himself,",2) | |
intro.display() | |
intro = Speech("italic","(He had started the whole thing himself, \r\n ** so that he knew all the possibilities in advance.)",2) | |
intro.display() | |
self.play() | |
if len(argv) > 1: | |
if argv[1] == "help": | |
help() | |
elif argv[1] == "custom": | |
misere, first_turn, board = argv[2],argv[3],argv[4] | |
board = [int(r) for r in argv[4:]] | |
game = Game(True,misere,first_turn,board,False) | |
game.start() | |
else: | |
print ("Only 'help' and 'custom' are valid first arguments.") | |
quit() | |
else: | |
custom, misere, first_turn, board = False,True,True,list([1,3,5,7]) | |
game = Game(custom,misere,first_turn,board,False) | |
game.start() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment