Skip to content

Instantly share code, notes, and snippets.

@GreatBahram
Last active December 24, 2020 10:02
Show Gist options
  • Save GreatBahram/2d94cea3462b35d6b20c4461241aa078 to your computer and use it in GitHub Desktop.
Save GreatBahram/2d94cea3462b35d6b20c4461241aa078 to your computer and use it in GitHub Desktop.
Beyond the basic stuff with Python - Hanoi
"""Four-in-a-Row, by Al Sweigart al@inventwithpython.com A tile-dropping game to get four-in-a-row, similar to Connect Four."""
import sys
from enum import Enum
from textwrap import dedent
class Cell(str, Enum):
EMPTY = "."
PLAYER_X = "X"
PLAYER_O = "O"
# Mine
# Constants used for displaying the board:
EMPTY_SPACE = "."
# A period is easier to count than a space.
PLAYER_X = "X"
PLAYER_O = "O"
# Note: Update BOARD_TEMPLATE & COLUMN_LABELS if BOARD_WIDTH is changed.
BOARD_WIDTH = 7
BOARD_HEIGHT = 6
COLUMN_LABELS = ("1", "2", "3", "4", "5", "6", "7")
assert len(COLUMN_LABELS) == BOARD_WIDTH
# The template string for displaying the board:
BOARD_TEMPLATE = dedent(
"""\
1234567
+-------+
|{}{}{}{}{}{}{}|
|{}{}{}{}{}{}{}|
|{}{}{}{}{}{}{}|
|{}{}{}{}{}{}{}|
|{}{}{}{}{}{}{}|
|{}{}{}{}{}{}{}|
+-------+"""
)
def main():
"""Runs a single game of Four-in-a-Row."""
print(
dedent(
"""\
Four-in-a-Row, by Al Sweigart al@inventwithpython.com Two players
take turns dropping tiles into one of seven columns, trying to make
Four-in-a-Row horizontally, vertically, or diagonally."""
)
)
# Set up a new game:
gameBoard = getNewBoard()
playerTurn = PLAYER_X
while True:
# Run a player's turn.
# Display the board and get player's move:
displayBoard(gameBoard)
playerMove = getPlayerMove(playerTurn, gameBoard)
gameBoard[playerMove] = playerTurn
# Check for a win or tie:
if isWinner(playerTurn, gameBoard):
displayBoard(gameBoard)
# Display the board one last time.
print("Player {} has won!".format(playerTurn))
sys.exit()
elif isFull(gameBoard):
displayBoard(gameBoard)
# Display the board one last time.
print("There is a tie!")
sys.exit()
# Switch turns to other player:
if playerTurn == PLAYER_X:
playerTurn = PLAYER_O
elif playerTurn == PLAYER_O:
playerTurn = PLAYER_X
def getNewBoard():
"""Returns a dictionary that represents a Four-in-a-Row board.
The keys are (columnIndex, rowIndex) tuples of two integers, and the
values are one of the "X", "O" or "." (empty space) strings."""
board = {}
for rowIndex in range(BOARD_HEIGHT):
for columnIndex in range(BOARD_WIDTH):
board[(columnIndex, rowIndex)] = EMPTY_SPACE
return board
def displayBoard(board):
"""Display the board and its tiles on the screen."""
# Prepare a list to pass to the format() string method for the board
# template. The list holds all of the board's tiles (and empty
# spaces) going left to right, top to bottom:
tileChars = []
for rowIndex in range(BOARD_HEIGHT):
for columnIndex in range(BOARD_WIDTH):
tileChars.append(board[(columnIndex, rowIndex)])
# Display the board:
print(BOARD_TEMPLATE.format(*tileChars))
def getPlayerMove(playerTile, board):
"""Let a player select a column on the board to drop a
tile into. Returns a tuple of the (column, row) that the tile falls
into."""
while True:
# Keep asking player until they enter a valid move.
print(f"Player {playerTile}, enter 1 to {BOARD_WIDTH} or QUIT:")
response = input("> ").upper().strip()
if response == "QUIT":
print("Thanks for playing!")
sys.exit()
if response not in COLUMN_LABELS:
print(f"Enter a number from 1 to {BOARD_WIDTH}.")
continue # Ask player again for their move.
columnIndex = int(response) - 1 # -1 for 0-based column indexes.
# If the column is full, ask for a move again:
if board[(columnIndex, 0)] != EMPTY_SPACE:
print("That column is full, select another one.")
continue
# Ask player again for their move.
# Starting from the bottom, find the first empty space.
for rowIndex in range(BOARD_HEIGHT - 1, -1, -1):
if board[(columnIndex, rowIndex)] == EMPTY_SPACE:
return (columnIndex, rowIndex)
def isFull(board):
"""Returns True if the `board` has no empty spaces, otherwise returns False."""
for rowIndex in range(BOARD_HEIGHT):
for columnIndex in range(BOARD_WIDTH):
if board[(columnIndex, rowIndex)] == EMPTY_SPACE:
return False # Found an empty space, so return False.
return True # All spaces are full
def isWinner(playerTile, board):
"""Returns True if `playerTile` has four tiles in a rowon `board`, otherwise returns False."""
# Go through the entire board, checking for four-in-a- row:
for columnIndex in range(BOARD_WIDTH - 3):
for rowIndex in range(BOARD_HEIGHT):
# Check for four-in-a-row going across to the right:
tile1 = board[(columnIndex, rowIndex)]
tile2 = board[(columnIndex + 1, rowIndex)]
tile3 = board[(columnIndex + 2, rowIndex)]
tile4 = board[(columnIndex + 3, rowIndex)]
if tile1 == tile2 == tile3 == tile4 == playerTile:
return True
for columnIndex in range(BOARD_WIDTH):
for rowIndex in range(BOARD_HEIGHT - 3):
# Check for four-in-a-row going down:
tile1 = board[(columnIndex, rowIndex)]
tile2 = board[(columnIndex, rowIndex + 1)]
tile3 = board[(columnIndex, rowIndex + 2)]
tile4 = board[(columnIndex, rowIndex + 3)]
if tile1 == tile2 == tile3 == tile4 == playerTile:
return True
for columnIndex in range(BOARD_WIDTH - 3):
for rowIndex in range(BOARD_HEIGHT - 3):
# Check for four-in-a-row going right-down diagonal:
tile1 = board[(columnIndex, rowIndex)]
tile2 = board[(columnIndex + 1, rowIndex + 1)]
tile3 = board[(columnIndex + 2, rowIndex + 2)]
tile4 = board[(columnIndex + 3, rowIndex + 3)]
if tile1 == tile2 == tile3 == tile4 == playerTile:
return True
# Check for four-in-a-row going left-down diagonal:
tile1 = board[(columnIndex + 3, rowIndex)]
tile2 = board[(columnIndex + 2, rowIndex + 1)]
tile3 = board[(columnIndex + 1, rowIndex + 2)]
tile4 = board[(columnIndex, rowIndex + 3)]
if tile1 == tile2 == tile3 == tile4 == playerTile:
return True
return False
# If this program was run (instead of imported), run the game:
if __name__ == "__main__":
main()
"""THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com A stack-moving puzzle game."""
import copy
import sys
from textwrap import dedent
from typing import Dict, List, Final
TOTAL_DISKS: Final = 5 # More disks means a more difficult puzzle.
SOLVED_TOWER: Final = list(range(TOTAL_DISKS, 0, -1))
def main():
"""Runs a single game of The Tower of Hanoi."""
print(
dedent(
"""\
THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com
Move the tower of disks, one disk at a time, to another
tower. Larger
disks cannot rest on top of a smaller disk.
More info at https://en.wikipedia.org/wiki/Tower_of_Hanoi
"""
)
)
"""The towers dictionary has keys "A", "B", and "C" and
values that are lists representing a tower of disks. The list
contains integers representing disks of different sizes, and the
start of the list is the bottom of the tower. For a game with 5
disks, the list [5, 4, 3, 2, 1] represents a completed tower.
The blank list [] represents a tower of no disks. The list [1, 3]
has a larger disk on top of a smaller disk and is an invalid configuration.
The list [3, 1] is allowed since smaller disks can go on top of larger ones."""
# Start with all disks on tower A:
towers: Dict[str, List[int]] = {"A": copy.copy(SOLVED_TOWER), "B": [], "C": []}
# Count the number of moves
move_nums: int = 0
while True: # Run a single turn on each iteration of this loop.
# Display the towers and disks:
displayTowers(towers)
# Ask the user for a move:
fromTower, toTower = getPlayerMove(towers)
# Move the top disk from fromTower to toTower:
disk = towers[fromTower].pop()
towers[toTower].append(disk)
move_nums += 1
# Check if the user has solved the puzzle:
if SOLVED_TOWER in (towers["B"], towers["C"]):
displayTowers(towers) # Display the towers one last time.
print(f"You have solved the puzzle in {move_nums} moves! Well done!")
print(f"It takes {(2**TOTAL_DISKS)-1} moves to solve it, at minimum.")
sys.exit()
def getPlayerMove(towers):
"""Asks the player for a move. Returns (fromTower, toTower)."""
while True: # Keep asking player until they enter a valid move.
print('Enter the letters of "from" and "to" towers, or QUIT.')
print("(e.g., AB to moves a disk from tower A to tower B.)")
print()
response = input("> ").upper().strip()
if response in ("QUIT", "Q"):
print("Thanks for playing!")
sys.exit()
# Make sure the user entered valid tower letters:
if response not in ("AB", "AC", "BA", "BC", "CA", "CB"):
print("Enter one of AB, AC, BA, BC, CA, or CB.")
continue
# Ask player again for their move.
# Use more descriptive variable names:
fromTower, toTower = response[0], response[1]
if len(towers[fromTower]) == 0:
# The "from" tower cannot be an empty tower:
print("You selected a tower with no disks.")
# Ask player again for their move.
continue
elif len(towers[toTower]) == 0:
# Any disk can be moved onto an empty "to" tower:
return fromTower, toTower
elif towers[toTower][-1] < towers[fromTower][-1]:
print("Can't put larger disks on top of smaller ones.")
# Ask player again for their move.
continue
else:
# This is a valid move, so return the selected towers:
return fromTower, toTower
def displayTowers(towers):
"""Display the three towers with their disks."""
# Display the three towers:
for level in range(TOTAL_DISKS, -1, -1):
for tower in (towers["A"], towers["B"], towers["C"]):
if level >= len(tower):
# Display the bare pole with no disk.
displayDisk(0)
else:
# Display thedisk.
displayDisk(tower[level])
print()
# Display the tower labels A, B, and C:
emptySpace = " " * (TOTAL_DISKS)
print("{0} A{0}{0} B{0}{0} C\n".format(emptySpace))
def displayDisk(width):
"""Display a disk of the given width. A width of 0 means no disk."""
emptySpace = " " * (TOTAL_DISKS - width)
if width == 0:
# Display a pole segment without a disk:
print(f"{emptySpace}||{emptySpace}", end="")
else:
# Display the disk:
disk = "@" * width
numLabel = str(width).rjust(2, "_")
print(f"{emptySpace}{disk}{numLabel}{disk}{emptySpace}", end="")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment