cli solitaire game

this is a simplified version of the popular game solitaire meant to be played via python idle

simple reformatting of this code can be made using python's pyinstaller module to produce a playable .exe

documentation can be found here



    import time
import random

class CardPile:

    def __init__(self):
        self.items = []

    def add_top(self, item):
        self.items.insert(0, item)

    def add_bottom(self, item):
        self.items.append(item)

    def remove_top(self):
        item = self.items.pop(0)
        return item

    def remove_bottom(self):
        item = self.items.pop(-1)
        return item

    def size(self):
        return len(self.items)

    def peek_top(self):
        return self.items[0]

    def peek_bottom(self):
        return self.items[-1]

    def print_all(self, index):
        if index == 0:
            shorter = len(self.items)-1
            print(self.items[0], " ".join(["*" for x in range(shorter)]))
        else:
            print(" ".join([str(i) for i in self.items]))


class Solitaire:

    def __init__(self, cards=[1,2,3,4,5,6]):
        self.piles = []
        self.num_cards = len(cards)
        self.num_piles = (self.num_cards // 8) + 3
        self.max_num_moves = self.num_cards * 2
        for i in range(self.num_piles):
            self.piles.append(CardPile())
        for i in range(self.num_cards):
            self.piles[0].add_bottom(cards[i])
        self.inventory = {1: [[random.randint(1,9)]],
                          2: [[1,2],[5,6],[6,7],[8,9],[4,3],[2,3]],
                          3: [[1, 2, 3], [3, 2, 1], [2, 3, 1]],
                          4: [[1, 2, 3, 4], [2, 3, 4, 5]],
                          5: [[5, 9, 8, 7, 6]],
                          6: [[5, 9, 8, 7, 6, 2]],
                          7: [[5, 9, 8, 7, 1, 2, 1]],
                          8: [[5, 9, 8, 7, 1, 2, 1, 3]],
                          9: [[5, 9, 8, 7, 6, 2, 1, 3, 4]],
                          10: [[5, 3, 9, 6, 12, 8, 11, 4, 10, 7]],
                          11: [[5, 3, 9, 6, 12, 8, 11, 4, 10, 7, 1]],
                          12: [[5, 3, 9, 6, 12, 8, 11, 4, 10, 7, 1, 2]],
                          13: [[5, 3, 9, 6, 12, 8, 11, 4, 10, 7, 1, 2, 8]]
                              }

    def reset_pile(self, cards):
        self.piles = []
        self.num_cards = len(cards)
        self.num_piles = (self.num_cards // 8) + 3
        self.max_num_moves = self.num_cards * 2
        for i in range(self.num_piles):
            self.piles.append(CardPile())
        for i in range(self.num_cards):
            self.piles[0].add_bottom(cards[i])

    def get_pile(self, i):
        return self.piles[i]

    def display(self):
        print('\n\n')
        for i, y in enumerate(self.piles):
            time.sleep(0.03)
            if i == 0:
                if y.size() != 0:
                    print("0: ", end="")
                    y.print_all(0)
                else:
                    print("0: ")
            elif y.size() == 0:
                print(str(i),":", sep="")
            if i != 0 and y.size() != 0:
                print(str(i),":", sep="", end=" ")
                y.print_all(1)
        print()
        time.sleep(0.2)

    def move(self, p1, p2):
        if self.piles[p1].size() > 0:
            pile_zero = self.piles[0]
            pile_one = self.piles[p1]
            pile_two = self.piles[p2]
            if p1 == 0 and p2 == 0:
                pile_zero.add_bottom(pile_zero.remove_top())
            elif p1 == 0 and p2 > 0:
                if pile_two.size() == 0:
                    pile_two.add_bottom(pile_zero.remove_top())
                elif pile_one.peek_top() == pile_two.peek_bottom() - 1:
                    pile_two.add_bottom(pile_zero.remove_top())
            else:
                if pile_one.size() != 0 and pile_two.size() != 0:
                    if p2 != 0:
                        if pile_one.peek_top() == pile_two.peek_bottom() - 1:
                            for i in range(pile_one.size()):
                                card_to_move = pile_one.remove_top()
                                pile_two.add_bottom(card_to_move)

    def is_complete(self):
        for index, pile in enumerate(self.piles):
            if pile.size() == self.num_cards and index != 0:
                return True
        return False

    def main_menu(self):
        time.sleep(0.1)
        print()
        print("""



                            MAIN MENU
        """)
        time.sleep(0.5)
        print()
        print("""                    1: Start a game of solitaire
        """)
        time.sleep(0.1)
        print("""                    2: Rules
        """)
        time.sleep(0.1)
        print("""                    0: Exit


        """)
        time.sleep(0.1)
        print()
        while True:
            try:
                user = int(input(
                    "CHOOSE AN OPTION (ENTER A NUMBER FROM ABOVE) >>> "))
                if user == 0 or user == 1 or user == 2:
                    time.sleep(0.3)
                    return int(user)
                time.sleep(0.2)
                print("Valid options are 0, 1 or 2\n")
                time.sleep(0.2)
            except:
                time.sleep(0.2)
                print("Valid options are 0, 1 or 2\n")
                time.sleep(0.2)

    def game_menu(self):
        time.sleep(0.2)
        print("\n\n\n")
        print("""            You have chosen to start a new game of Solitaire!
        \n\n\n""")
        time.sleep(0.2)
        print("""               1: Random cards (may not be winnable)
        """)
        time.sleep(0.2)
        print("""               2: Pre-existing set (length 1 to 13)
        """)
        time.sleep(0.2)
        print("""               0: Return to main menu


        """)
        time.sleep(0.2)
        while True:
            try:
                user = int(input(
                    "CHOOSE AN OPTION (ENTER A NUMBER FROM ABOVE) >>> "))
                if user == 0 or user == 1 or user == 2:
                    break
                time.sleep(0.2)
                print("Valid options are 0, 1 or 2\n")
                time.sleep(0.2)
            except:
                time.sleep(0.2)
                print("Valid options are 0, 1 or 2\n")
                time.sleep(0.2)
        time.sleep(0.3)
        if int(user) == 1:
            card_hand = []
            time.sleep(0.2)
            print("""


      _                                _           _          _
     | |__  _ __ __ ___   _____    ___| |__   ___ (_) ___ ___| |
     | '_ \| '__/ _` \ \ / / _ \  / __| '_ \ / _ \| |/ __/ _ \ |
     | |_) | | | (_| |\ V /  __/ | (__| | | | (_) | | (_|  __/_|
     |_.__/|_|  \__,_| \_/ \___|  \___|_| |_|\___/|_|\___\___(_)



            """)
            time.sleep(1)
            print()
            while True:
                try:
                    user = int(input(
                        "CHOOSE YOUR HAND'S SIZE (ENTER A NUMBER) >>> "))
                    if int(user) > 0:
                        break
                    time.sleep(0.2)
                    print("Valid options are lengths above 0\n")
                    time.sleep(0.2)
                except:
                    time.sleep(0.2)
                    print("Valid options are lengths above 0\n")
                    time.sleep(0.2)
            random_number = random.randint(1, int(user))
            while len(card_hand) != int(user):
                if random_number not in card_hand:
                    card_hand.append(random_number)
                random_number = random.randint(1, int(user))
            self.reset_pile(card_hand)
        elif int(user) == 2:
            time.sleep(0.2)
            print()
            while True:
                try:
                    user = int(input(
                        "CHOOSE THE SIZE OF YOUR HAND (ENTER A NUMBER) >> "))
                    if int(user) > 0:
                        break
                    time.sleep(0.2)
                    print("Va1id options are numbers (inclusive) 1 to 13")
                    time.sleep(0.2)
                except:
                    time.sleep(0.2)
                    print("Va1id options are numbers (inclusive) 1 to 13")
                    time.sleep(0.2)
            hand_options = self.inventory[int(user)]
            chosen_hand = random.randint(0, len(hand_options) - 1)
            self.reset_pile(hand_options[chosen_hand])
        elif int(user) == 0:
            return 5

    def game(self):
        state = self.game_menu()
        if state == 5:
            return state
        print("""\n\n\n\n
      ************************ NEW GAME **************************
        \n\n\n""")
        time.sleep(0.5)
        print()
        print()
        move_number = 1
        max_move = self.max_num_moves
        while move_number <= max_move and self.is_complete() == False:
                self.display()
                max_moves = self.max_num_moves
                print("ROUND", move_number, "OUT OF", max_moves)
                print("Remember you can exit at any point by entering '-1'\n")
                while True:
                    try:
                        row1 = int(input(
                            "\nMove a card from pile number: "),10)
                        if row1 == -1:
                            print("\n\nGame Interrupted, ",end="")
                            print("returning to main menu...")
                            time.sleep(2)
                            return 5
                        if row1 <= len(self.piles) -1 and row1 > -1:
                            break
                        print("Enter a valid pile number!")
                        time.sleep(0.2)
                    except:
                        print("Enter a valid pile number!")
                        time.sleep(0.2)
                while True:
                    try:
                        row2 = int(input(
                            "\nMove a card to pile number: "),10)
                        if row2 == -1:
                            print("\n\nGame Interrupted, ", end="")
                            print("returning to main menu...")
                            time.sleep(2)
                            return 5
                        if row2 <= len(self.piles) -1 and row2 > -1:
                            break
                        print("Enter a valid pile number!")
                        time.sleep(0.2)
                    except:
                        print("Enter a valid pile number!")
                        time.sleep(0.2)
                if row1 >= 0 and row2 >= 0:
                    if row1 < self.num_piles and row2 < self.num_piles:
                        if self.piles[row1].size() == 0 and row1 != 0:
                            if self.piles[row2].size() == 0 and row2 != 0:
                                print("\nThis move did not achieve anything!")
                        else:
                            self.move(row1, row2)
                            move_number += 1
        if self.is_complete():
            time.sleep(0.5)
            print("""

   ____                            _         _       _   _                 _
  / ___|___  _ __   __ _ _ __ __ _| |_ _   _| | __ _| |_(_) ___  _ __  ___| |
 | |   / _ \| '_ \ / _` | '__/ _` | __| | | | |/ _` | __| |/ _ \| '_ \/ __| |
 | |__| (_) | | | | (_| | | | (_| | |_| |_| | | (_| | |_| | (_) | | | \__ \_|
  \____\___/|_| |_|\__, |_|  \__,_|\__|\__,_|_|\__,_|\__|_|\___/|_| |_|___(_)
                   |___/

        """)
            time.sleep(0.5)
            print()
            if move_number == 2:

                print("You won in", move_number - 1, "step!\n")
            else:
                print("You won in", move_number - 1, "steps!\n")
        else:
            print("""

  _____                               _       _
 |_   _| __ _   _    __ _  __ _  __ _(_)_ __ | |
   | || '__| | | |  / _` |/ _` |/ _` | | '_ \| |
   | || |  | |_| | | (_| | (_| | (_| | | | | |_|
   |_||_|   \__, |  \__,_|\__, |\__,_|_|_| |_(_)
            |___/         |___/

            """)
            print("You lost, better luck next time!\n")
        time.sleep(1)
        print()
        user = input("Enter 1 to play again or 0 to exit to main menu >>> ")
        print("\n\n\n")
        if int(user) == 0:
            return 5
        else:
            return 1

    def play(self):
        print("""
          ____   ___  _     ___ _____  _    ___ ____  _____
         / ___| / _ \| |   |_ _|_   _|/ \  |_ _|  _ \| ____|
         \___ \| | | | |    | |  | | / _ \  | || |_) |  _|
          ___) | |_| | |___ | |  | |/ ___ \ | ||  _ <| |___
         |____/ \___/|_____|___| |_/_/   \_\___|_| \_\_____|

        """)
        print()
        time.sleep(1)
        entry_to_game = self.main_menu()
        while entry_to_game != 0:
            if entry_to_game == 1:
                current_state = 1
                while current_state == 1:
                    current_state = self.game()
            elif entry_to_game ==2:
                current_state = 99
                while current_state == 99:
                    current_state = self.rules()
            entry_to_game = self.main_menu()
        time.sleep(0.5)
        print("""




              _____ _   _    _    _   _ _  ______
             |_   _| | | |  / \  | \ | | |/ / ___|
               | | | |_| | / _ \ |  \| | ' /\___ \
               | | |  _  |/ ___ \| |\  | . \ ___) |
               |_| |_| |_/_/   \_\_| \_|_|\_\____/

                        _____ ___  ____
                       |  ___/ _ \|  _ \
                       | |_ | | | | |_) |
                       |  _|| |_| |  _ <
                       |_|   \___/|_| \_\

              ____  _        _  __   _____ _   _  ____
             |  _ \| |      / \ \ \ / /_ _| \ | |/ ___|
             | |_) | |     / _ \ \ V / | ||  \| | |  _
             |  __/| |___ / ___ \ | |  | || |\  | |_| |
             |_|   |_____/_/   \_\ _| |___|_| \_|\____|





        """)

    def rules(self):
        print("""



                The rules to Solitaire (or at least this version of it)
            """)
        time.sleep(0.05)
        print("""
        You will presented with a hand of cards containing shuffled
        numbers, although there is a small chance you can get consecutive
        hands in the random hand game mode.
            """)
        time.sleep(0.05)
        print("""
        Apart from the first card at the top of the hand, all its other
        cards will be hidden.
            """)
        time.sleep(0.05)
        print("""
        At the start of the game and at any point in the game, assuming you
        still have moves left, you can shuffle through the hand which will
        be placed in pile 0, at the top of the other piles.
            """)
        time.sleep(0.05)
        print("""
        Your goal is to rearrange the cards from pile 0 into a single other
        pile which must be in descending order (going from the biggest card
        to the smallest card).
            """)
        time.sleep(0.05)
        print("""
        When you have emptied pile 0 and that the cards that were in pile 0
        are now all rearranged in a single other pile according to the
        required order, you have won the game!
            """)
        time.sleep(0.05)
        print("""
        Every round you will have the opportunity to move cards from one
        pile to another or to rotate through the hand.
            """)
        time.sleep(0.05)
        print("""
        Apart from the first card at the top of the hand, all its other
        cards will be hidden.
            """)
        time.sleep(0.05)
        print("""
        Exceeding the number of available rounds without solving the
        solitaire game will result in defeat.
            """)
        time.sleep(0.05)
        print("""
        At any round, to return to the main menu, enter "-1".

            Have fun!


            """)
        time.sleep(1)
        print()
        while True:
            try:
                user = int(input(
                    "ENTER 0 TO RETURN TO MAIN MENU >> "))
                if user == 0:
                    return 5
                time.sleep(0.2)
                print("Entering 0 will get you to the main menu\n")
                time.sleep(0.2)
            except:
                time.sleep(0.2)
                print("Entering 0 will get you to the main menu\n")
                time.sleep(0.2)
        time.sleep(0.3)



Solitaire().play()

back to reception