Skip to content

Instantly share code, notes, and snippets.

@subsetpark
Forked from scottmries/Nim
Last active August 29, 2015 14:18
Show Gist options
  • Save subsetpark/9c28d9f58ad8e2be3af1 to your computer and use it in GitHub Desktop.
Save subsetpark/9c28d9f58ad8e2be3af1 to your computer and use it in GitHub Desktop.
# 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