Skip to content

Instantly share code, notes, and snippets.

@subsetpark
Forked from scottmries/Nim
Last active August 29, 2015 14:18

Revisions

  1. subsetpark revised this gist Apr 2, 2015. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Nim
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,6 @@
    # coding=utf-8


    from sys import argv
    import random, time

  2. subsetpark revised this gist Apr 2, 2015. No changes.
  3. subsetpark revised this gist Apr 2, 2015. No changes.
  4. @scottmries scottmries created this gist Apr 2, 2015.
    343 changes: 343 additions & 0 deletions Nim
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,343 @@
    # 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()