Last active
November 4, 2021 20:32
-
-
Save danbst/ae4fd7cbb1ea97d5efbc80bd06b50075 to your computer and use it in GitHub Desktop.
Пачка програм
This file contains 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
""" | |
Алфавіт-квіз, автор Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import random, time | |
QUESTION_SIZE = 5 | |
QUIZ_DURATION = 30 | |
abc = {} | |
abc['uk'] = 'АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩьЮЯ' | |
abc['small'] = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩьЮЯ' | |
abc['eng'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
ALPHABET = abc['small'] | |
REVERSE_ALPHABET = abc['small'][::-1] | |
preamble = ''' | |
\u001b[32m | |
Швидко надрукуй літери в алфавітному порядку. | |
У тебе є {} секунд! | |
Наприклад: | |
П М О Т К <- букви | |
> кмопт <- їх правильний порядок | |
\u001b[0m | |
''' | |
def main(): | |
slowPrint(ALPHABET, 0.02) | |
slowPrint(' Алфавіт Квіз', 0.02) | |
slowPrint(REVERSE_ALPHABET, 0.02) | |
time.sleep(0.5) | |
print(preamble.format(QUIZ_DURATION)) | |
input('Тисни Enter щоб почати...') | |
startTime = time.time() | |
numCorrect = 0 | |
while True: | |
quizLetters = random.sample(ALPHABET, QUESTION_SIZE) | |
print(' '.join(quizLetters)) | |
response = input('> ').strip().upper() | |
response = response.replace(' ', '') # Remove spaces. | |
# Check if the quiz's time is up: | |
if time.time() - QUIZ_DURATION > startTime: | |
print("ЧАС ВИЙШОВ!") | |
break | |
# Check if the response is correct: | |
if list(response) == sorted(quizLetters): | |
print(' Вірно!\n') | |
numCorrect += 1 # Increase the score by 1. | |
else: | |
print(' Упс, помилка. :(\n') | |
# After the loop exits, the quiz is over. Show the final score: | |
print('За {} секунд ти'.format(QUIZ_DURATION)) | |
print('маєш {} правильних результатів!'.format(numCorrect)) | |
print('Дякую за гру!') | |
def slowPrint(text, pauseAmount=0.1): | |
"""Slowly print out the characters in text one at a time.""" | |
for character in text: | |
# Set flush=True here so the text is immediately printed: | |
print(character, flush=True, end='') # end='' means no newline. | |
time.sleep(pauseAmount) # Pause in between each character. | |
print() # Print a newline. | |
# If this program was run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
""" | |
Баглі, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import random | |
NUM_DIGITS = 3 | |
MAX_GUESSES = 10 | |
preamble = ''' | |
Баглі, лоігчна гра | |
Я загадую {}-значне число, в якому цифри не повторюються. | |
Спробуй його відгадати. Ось підказки: | |
Якщо я кажу: то це означає: | |
----------- ------------- | |
Піко одна цифра правильна, але у неправильному місці | |
Фермі одна цифра правильна, і у правильному місці | |
Баглі не вгадано жодну цифру | |
Наприклад, якщо секретне число 248, а ти кажеш 843, то підказки будуть | |
Фермі Піко | |
''' | |
def main(): | |
print(preamble.format(NUM_DIGITS)) | |
while True: | |
secretNum = getSecretNum() | |
print('Я задумав число.') | |
print(' У тебе є {} спроб, щоб його вгадати.'.format(MAX_GUESSES)) | |
numGuesses = 1 | |
while numGuesses <= MAX_GUESSES: | |
guess = '' | |
# Keep looping until they enter a valid guess: | |
while len(guess) != NUM_DIGITS or not guess.isdecimal(): | |
print('Спроба №{}: '.format(numGuesses)) | |
guess = input('> ') | |
clues = getClues(guess, secretNum) | |
print(clues) | |
numGuesses += 1 | |
if guess == secretNum: | |
break # They're correct, so break out of this loop. | |
if numGuesses > MAX_GUESSES: | |
print('Більше немає спроб.') | |
print('Відповідь -- {}.'.format(secretNum)) | |
# Ask player if they want to play again. | |
print('Хочеш зіграти ще раз? (yes/no так/ні)') | |
answer = input('> ').lower().startswith | |
if not (answer('y') or answer('т')): | |
break | |
print('Дякую за гру!') | |
def getSecretNum(): | |
"""Returns a string made up of NUM_DIGITS unique random digits.""" | |
numbers = list('0123456789') # Create a list of digits 0 to 9. | |
random.shuffle(numbers) # Shuffle them into random order. | |
# Get the first NUM_DIGITS digits in the list for the secret number: | |
secretNum = '' | |
for i in range(NUM_DIGITS): | |
secretNum += str(numbers[i]) | |
return secretNum | |
def getClues(guess, secretNum): | |
"""Returns a string with the pico, fermi, bagels clues for a guess | |
and secret number pair.""" | |
if guess == secretNum: | |
return 'You got it!' | |
clues = [] | |
for i in range(len(guess)): | |
if guess[i] == secretNum[i]: | |
# A correct digit is in the correct place. | |
clues.append('Фермі') | |
elif guess[i] in secretNum: | |
# A correct digit is in the incorrect place. | |
clues.append('Піко') | |
if len(clues) == 0: | |
return 'Баглі' # There are no correct digits at all. | |
else: | |
# Sort the clues into alphabetical order so their original order | |
# doesn't give information away. | |
clues.sort() | |
# Make a single string from the list of string clues. | |
return ' '.join(clues) | |
# If the program is run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
import turtle | |
import random | |
verts = [(-200, 0), (200, -200), (0, 200)] | |
turtle.bgcolor('black') | |
turtle.tracer(0, 0) | |
turtle.penup() | |
turtle.goto(-20, -71) | |
turtle.shape('circle') | |
turtle.shapesize(2/20) | |
rr = random.randrange | |
rrd = random.random | |
kx = ky = 0.5 | |
cnt = 0 | |
while True: | |
cnt += 1 | |
vx, vy = random.choice(verts) | |
x, y = turtle.pos() | |
turtle.goto((x+vx)*kx, (y+vy)*ky) | |
turtle.color(rrd(), rrd(), rrd()) | |
turtle.stamp() | |
if cnt % 50 == 0: | |
turtle.update() | |
if cnt > 1000: | |
verts = [(rr(-400, 400), rr(-350, 350)) for _ in range(3)] | |
kx = ky = rrd() | |
turtle.clear() | |
cnt = 0 |
This file contains 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
""" | |
Парадокс днів народження, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import datetime | |
import random | |
def getBirthdays(numberOfBirthdays): | |
"""Returns a list of number random date objects for birthdays.""" | |
birthdays = [] | |
for i in range(numberOfBirthdays): | |
# The year is unimportant for our simulation, as long as all | |
# birthdays have the same year. | |
startOfYear = datetime.date(2001, 1, 1) | |
# Get a random day into the year: | |
randomNumberOfDays = datetime.timedelta(random.randint(0, 364)) | |
birthday = startOfYear + randomNumberOfDays | |
birthdays.append(birthday) | |
return birthdays | |
def getMatch(birthdays): | |
"""Returns the date object of a birthday that occurs more than once | |
in the birthdays list.""" | |
if len(birthdays) == len(set(birthdays)): | |
return None # All birthdays are unique, so return None. | |
# Compare each birthday to every other birthday: | |
for a, birthdayA in enumerate(birthdays): | |
for b, birthdayB in enumerate(birthdays[a + 1 :]): | |
if birthdayA == birthdayB: | |
return birthdayA # Return the matching birthday. | |
# Display the intro: | |
print(''' | |
Як часто в групі з 30 учнів у двох учнів день народження в один день? | |
Відповідь дивує --- у більшості класів! | |
Ця програма робить симуляцію (створює багато випадкових груп), щоб | |
порахувати цей парадокс більш точно. | |
''') | |
# Set up a tuple of month names in order: | |
MONTHS = ('Січ', 'Лют', 'Бер', 'Кві', 'Тра', 'Чер', | |
'Лип', 'Сер', 'Вер', 'Жов', 'Лис', 'Гру') | |
while True: # Keep asking until the user enters a valid amount. | |
print('Скільки учнів в групі? (не більше 100)') | |
response = input('> ') | |
if response.isdecimal() and (0 < int(response) <= 100): | |
numBDays = int(response) | |
break # User has entered a valid amount. | |
print() | |
# Generate and display the birthdays: | |
print('Ось', numBDays, 'випадкових днів народжень:') | |
birthdays = getBirthdays(numBDays) | |
for i, birthday in enumerate(birthdays): | |
if i != 0: | |
# Display a comma for each birthday after the first birthday. | |
print(', ', end='') | |
monthName = MONTHS[birthday.month - 1] | |
dateText = '{} {}'.format(monthName, birthday.day) | |
print(dateText, end='') | |
print() | |
print() | |
# Determine if there are two birthdays that match. | |
match = getMatch(birthdays) | |
# Display the results: | |
print('Якщо порахувати, то ', end='') | |
if match != None: | |
monthName = MONTHS[match.month - 1] | |
dateText = '{} {}'.format(monthName, match.day) | |
print('кілька учнів мають день народження', dateText) | |
else: | |
print('дні народження у всіх різні.') | |
print() | |
# Run through 100,000 simulations: | |
print('Генерую', numBDays, 'випадкових днів народжень 100 000 разів...') | |
input('Тисни Enter щоб продовжити...') | |
#print('Let\'s run another 100,000 simulations.') | |
simMatch = 0 # How many simulations had matching birthdays in them. | |
for i in range(100_000): | |
# Report on the progress every 10,000 simulations: | |
if i % 10_000 == 0: | |
print(i, 'симуляцій зроблено...') | |
birthdays = getBirthdays(numBDays) | |
if getMatch(birthdays) != None: | |
simMatch = simMatch + 1 | |
print('100 000 симуляцій закінчено.') | |
# Display simulation results: | |
probability = round(simMatch / 100_000 * 100, 2) | |
print(f''' | |
З {100_000} симуляцій груп по {numBDays} учнів, день народження | |
в один день був в {simMatch} групах. Це означає, що {numBDays} учнів | |
мають {probability}% шанс разом святкувати ДР. | |
Це може бути неочікувно! | |
''') |
This file contains 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
""" | |
Текст картинкою, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import sys | |
bitmap = """ | |
.................................................................... | |
************** * *** ** * ****************************** | |
********************* ** ** * * ****************************** * | |
** ***************** ****************************** | |
************* ** * **** ** ************** * | |
********* ******* **************** * * | |
******** *************************** * | |
* * **** *** *************** ****** ** * | |
**** * *************** *** *** * | |
****** ************* ** ** * | |
******** ************* * ** *** | |
******** ******** * *** **** | |
********* ****** * **** ** * ** | |
********* ****** * * *** * * | |
****** ***** ** ***** * | |
***** **** * ******** | |
***** **** ********* | |
**** ** ******* * | |
*** * * | |
** * * | |
....................................................................""" | |
print('Напиши текст, який ти хочеш закодувати картинкою:') | |
message = input('> ') | |
if message == '': | |
sys.exit() | |
# Loop over each line in the bitmap: | |
for line in bitmap.splitlines(): | |
# Loop over each character in the line: | |
for i, bit in enumerate(line): | |
if bit == ' ': | |
# Print an empty space since there's a space in the bitmap: | |
print(' ', end='') | |
else: | |
# Print a character from the messge: | |
print(message[i % len(message)], end='') | |
print() # Print a newline. |
This file contains 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
""" | |
Календар, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import datetime | |
# Set up the constants: | |
DAYS = ('Понеділок', 'Вівторок', 'Середа', 'Четвер', 'П\'ятниця', | |
'Субота', 'Неділя') | |
MONTHS = ('Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', | |
'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень') | |
R = '\u001b[0m' | |
background = '\u001b[7m\u001b[1m' | |
width = 7 | |
print('Календарик') | |
year, month = None, None | |
while not year: # Loop to get a year from the user. | |
print('Введи рік для календаря:') | |
response = input('> ') | |
if response.isdecimal() and int(response) > 0: | |
year = int(response) | |
break | |
print('Введи рік числом, наприклад, 2023.') | |
continue | |
while not month: # Loop to get a month from the user. | |
print('Введи місяць, 1-12:') | |
response = input('> ') | |
if not response.isdecimal(): | |
print('Введи місяць числом, наприклад 3 для березня.') | |
continue | |
month = int(response) | |
if 1 <= month <= 12: | |
break | |
print('Має бути число від 1 до 12.') | |
month = None | |
def getCalendarFor(year, month): | |
calText = '' # calText will contain the string of our calendar. | |
# Put the month and year at the top of the calendar: | |
calText += (' ' * 34) + MONTHS[month - 1] + ' ' + str(year) + '\n' | |
# Add the days of the week labels to the calendar: | |
# (!) Try changing this to abbreviations: SUN, MON, TUE, etc. | |
calText += ''.join([day[:width-1].ljust(width+1, '.') for day in DAYS]) + '\n' | |
# The horizontal line string that separate weeks: | |
weekSeparator = (('+' + '-'*width) * 7) + '+\n' | |
# The blank rows have ten spaces in between the | day separators: | |
blankRow = (('|' + ' '*width) * 7) + '|\n' | |
# Get the first date in the month. (The datetime module handles all | |
# the complicated calendar stuff for us here.) | |
currentDate = datetime.date(year, month, 1) | |
# Roll back currentDate until it is Sunday. (weekday() returns 6 | |
# for Sunday, not 0.) | |
while currentDate.weekday() != 0: | |
currentDate -= datetime.timedelta(days=1) | |
while True: # Loop over each week in the month. | |
calText += weekSeparator | |
# dayNumberRow is the row with the day number labels: | |
dayNumberRow = '' | |
for i in range(7): | |
dayNumberLabel = str(currentDate.day).rjust(2) | |
dayNumberRow += '|' | |
filler = dayNumberLabel + (' ' * (width-2)) | |
if currentDate.month == month: | |
dayNumberRow += background + filler + R | |
else: | |
dayNumberRow += filler | |
currentDate += datetime.timedelta(days=1) # Go to next day. | |
dayNumberRow += '|\n' # Add the vertical line after Saturday. | |
# Add the day number row and 3 blank rows to the calendar text. | |
calText += dayNumberRow | |
for i in range(3): # (!) Try changing the 4 to a 5 or 10. | |
calText += blankRow | |
# Check if we're done with the month: | |
if currentDate.month != month: | |
break | |
# Add the horizontal line at the very bottom of the calendar. | |
calText += weekSeparator | |
return calText | |
calText = getCalendarFor(year, month) | |
print(calText) # Display the calendar. | |
# Save the calendar to a text file: | |
calendarFilename = 'calendar_{}_{}.txt'.format(year, month) | |
with open(calendarFilename, 'w') as fileObj: | |
fileObj.write(calText) | |
print('Saved to ' + calendarFilename) |
This file contains 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
"""Clickbait Headline Generator, by Al Sweigart [email protected] | |
A clickbait headline generator for your soulless content farm website. | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, beginner, humor, word""" | |
__version__ = 0 | |
import random | |
# Set up the constants: | |
OBJECT_PRONOUNS = ['Her', 'Him', 'Them'] | |
POSSESIVE_PRONOUNS = ['Her', 'His', 'Their'] | |
PERSONAL_PRONOUNS = ['She', 'He', 'They'] | |
STATES = ['California', 'Texas', 'Florida', 'New York', 'Pennsylvania', | |
'Illinois', 'Ohio', 'Georgia', 'North Carolina', 'Michigan'] | |
NOUNS = ['Athlete', 'Clown', 'Shovel', 'Paleo Diet', 'Doctor', 'Parent', | |
'Cat', 'Dog', 'Chicken', 'Robot', 'Video Game', 'Avocado', | |
'Plastic Straw','Serial Killer', 'Telephone Psychic'] | |
PLACES = ['House', 'Attic', 'Bank Deposit Box', 'School', 'Basement', | |
'Workplace', 'Donut Shop', 'Apocalypse Bunker'] | |
WHEN = ['Soon', 'This Year', 'Later Today', 'RIGHT NOW', 'Next Week'] | |
def main(): | |
print('Clickbait Headline Generator') | |
print('By Al Sweigart [email protected]') | |
print() | |
print('Our website needs to trick people into looking at ads!') | |
while True: | |
print('Enter the number of clickbait headlines to generate:') | |
response = input('> ') | |
if not response.isdecimal(): | |
print('Please enter a number.') | |
else: | |
numberOfHeadlines = int(response) | |
break # Exit the loop once a valid number is entered. | |
for i in range(numberOfHeadlines): | |
clickbaitType = random.randint(1, 8) | |
if clickbaitType == 1: | |
headline = generateAreMillenialsKillingHeadline() | |
elif clickbaitType == 2: | |
headline = generateWhatYouDontKnowHeadline() | |
elif clickbaitType == 3: | |
headline = generateBigCompaniesHateHerHeadline() | |
elif clickbaitType == 4: | |
headline = generateYouWontBelieveHeadline() | |
elif clickbaitType == 5: | |
headline = generateDontWantYouToKnowHeadline() | |
elif clickbaitType == 6: | |
headline = generateGiftIdeaHeadline() | |
elif clickbaitType == 7: | |
headline = generateReasonsWhyHeadline() | |
elif clickbaitType == 8: | |
headline = generateJobAutomatedHeadline() | |
print(headline) | |
print() | |
website = random.choice(['wobsite', 'blag', 'Facebuuk', 'Googles', | |
'Facesbook', 'Tweedie', 'Pastagram']) | |
when = random.choice(WHEN).lower() | |
print('Post these to our', website, when, 'or you\'re fired!') | |
# Each of these functions returns a different type of headline: | |
def generateAreMillenialsKillingHeadline(): | |
noun = random.choice(NOUNS) | |
return 'Are Millenials Killing the {} Industry?'.format(noun) | |
def generateWhatYouDontKnowHeadline(): | |
noun = random.choice(NOUNS) | |
pluralNoun = random.choice(NOUNS) + 's' | |
when = random.choice(WHEN) | |
return 'Without This {}, {} Could Kill You {}'.format(noun, pluralNoun, when) | |
def generateBigCompaniesHateHerHeadline(): | |
pronoun = random.choice(OBJECT_PRONOUNS) | |
state = random.choice(STATES) | |
noun1 = random.choice(NOUNS) | |
noun2 = random.choice(NOUNS) | |
return 'Big Companies Hate {}! See How This {} {} Invented a Cheaper {}'.format(pronoun, state, noun1, noun2) | |
def generateYouWontBelieveHeadline(): | |
state = random.choice(STATES) | |
noun = random.choice(NOUNS) | |
pronoun = random.choice(POSSESIVE_PRONOUNS) | |
place = random.choice(PLACES) | |
return 'You Won\'t Believe What This {} {} Found in {} {}'.format(state, noun, pronoun, place) | |
def generateDontWantYouToKnowHeadline(): | |
pluralNoun1 = random.choice(NOUNS) + 's' | |
pluralNoun2 = random.choice(NOUNS) + 's' | |
return 'What {} Don\'t Want You To Know About {}'.format(pluralNoun1, pluralNoun2) | |
def generateGiftIdeaHeadline(): | |
number = random.randint(7, 15) | |
noun = random.choice(NOUNS) | |
state = random.choice(STATES) | |
return '{} Gift Ideas to Give Your {} From {}'.format(number, noun, state) | |
def generateReasonsWhyHeadline(): | |
number1 = random.randint(3, 19) | |
pluralNoun = random.choice(NOUNS) + 's' | |
# number2 should be no larger than number1: | |
number2 = random.randint(1, number1) | |
return '{} Reasons Why {} Are More Interesting Than You Think (Number {} Will Surprise You!)'.format(number1, pluralNoun, number2) | |
def generateJobAutomatedHeadline(): | |
state = random.choice(STATES) | |
noun = random.choice(NOUNS) | |
i = random.randint(0, 2) | |
pronoun1 = POSSESIVE_PRONOUNS[i] | |
pronoun2 = PERSONAL_PRONOUNS[i] | |
if pronoun1 == 'Their': | |
return 'This {} {} Didn\'t Think Robots Would Take {} Job. {} Were Wrong.'.format(state, noun, pronoun1, pronoun2) | |
else: | |
return 'This {} {} Didn\'t Think Robots Would Take {} Job. {} Was Wrong.'.format(state, noun, pronoun1, pronoun2) | |
# If the program is run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
""" | |
Підкадання монетки, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import random | |
print('Підкидання монетки') | |
# Ask the user how many flips to make: | |
print('Скільки разів кидати монетку?') | |
while True: | |
response = input('> ') | |
if response.isdecimal(): | |
numberOfFlips = int(response) | |
break # Exit the loop once they enter a valid number. | |
# The streakStats dictionary keeps count of how many times a certain | |
# streak of heads or tails has occurred. The keys are tuples of | |
# (streakLength, side) and the values are integer counts. | |
streakStats = {} | |
for i in range(numberOfFlips): | |
# Simulate one coin flip: | |
if random.randint(0, 1) == 0: | |
flip = 'герби' | |
else: | |
flip = 'числа' | |
print(flip[0], end='') # Print out "h" or "t". | |
isFirstFlip = i == 0 | |
if isFirstFlip: | |
currentStreakLength = 0 | |
currentStreakSide = flip | |
# Check if we need to reset the streak: | |
if flip != currentStreakSide: | |
# Record the streak stats: | |
streakKey = (currentStreakLength, currentStreakSide) | |
if streakKey not in streakStats: | |
# Set this new key to 0: | |
streakStats[streakKey] = 0 | |
streakStats[streakKey] = streakStats[streakKey] + 1 | |
# Reset the streak length for this new streak: | |
currentStreakLength = 1 | |
currentStreakSide = flip | |
else: | |
currentStreakLength = currentStreakLength + 1 | |
# Record the streak stats for the final flip: | |
streakKey = (currentStreakLength, currentStreakSide) | |
if streakKey not in streakStats: | |
streakStats[streakKey] = 0 # New streaks start at 0. | |
streakStats[streakKey] = streakStats[streakKey] + 1 | |
print() | |
print('Кінець.') | |
streakLengthsAndSides = list(streakStats.keys()) | |
streakLengthsAndSides.sort() | |
# Display the results: | |
for length, side in streakLengthsAndSides: | |
label = str(length) + ' ' + side + ' підряд' | |
print(label.rjust(21) + ' - ' + str(streakStats[(length, side)])) |
This file contains 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
"""Conway's Game of Life, by Al Sweigart [email protected] | |
The classic cellular automata simulation. Press Ctrl-C to stop. | |
More info at: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life | |
This and other games are available at https://nostarch.com/XX | |
Tags: short, artistic, simulation""" | |
__version__ = 0 | |
import copy, random, sys, time | |
# Set up the constants: | |
WIDTH = 50 # The width of the cell grid. | |
HEIGHT = 20 # The height of the cell grid. | |
# (!) Try changing ALIVE to '#' or another character: | |
ALIVE = '.' # The character representing a living cell. | |
# (!) Try changing DEAD to '.' or another character: | |
DEAD = ' ' # The character representing a dead cell. | |
# (!) Try changing ALIVE to '|' and DEAD to '-'. | |
# The cells and nextCells are dictionaries for the state of the game. | |
# Their keys are (x, y) tuples and their values are one of the ALIVE | |
# or DEAD values. | |
nextCells = {} | |
# Put random dead and alive cells into nextCells: | |
for x in range(WIDTH): # Loop over every possible column. | |
for y in range(HEIGHT): # Loop over every possible row. | |
# 50/50 chance for starting cells being alive or dead. | |
if random.randint(0, 1) == 0: | |
nextCells[(x, y)] = ALIVE # Add a living cell. | |
else: | |
nextCells[(x, y)] = DEAD # Add a dead cell. | |
while True: # Main program loop. | |
# Each iteration of this loop is a step of the simulation. | |
print('\n' * 50) # Separate each step with newlines. | |
cells = copy.deepcopy(nextCells) | |
# Print cells on the screen: | |
for y in range(HEIGHT): | |
for x in range(WIDTH): | |
print(cells[(x, y)], end='') # Print the # or space. | |
print() # Print a newline at the end of the row. | |
print('Press Ctrl-C to quit.') | |
# Calculate the next step's cells based on current step's cells: | |
for x in range(WIDTH): | |
for y in range(HEIGHT): | |
# Get the neighboring coordinates of (x, y), even if they | |
# wrap around the edge: | |
left = (x - 1) % WIDTH | |
right = (x + 1) % WIDTH | |
above = (y - 1) % HEIGHT | |
below = (y + 1) % HEIGHT | |
# Count the number of living neighbors: | |
numNeighbors = 0 | |
if cells[(left, above)] == ALIVE: | |
numNeighbors += 1 # Top-left neighbor is alive. | |
if cells[(x, above)] == ALIVE: | |
numNeighbors += 1 # Top neighbor is alive. | |
if cells[(right, above)] == ALIVE: | |
numNeighbors += 1 # Top-right neighbor is alive. | |
if cells[(left, y)] == ALIVE: | |
numNeighbors += 1 # Left neighbor is alive. | |
if cells[(right, y)] == ALIVE: | |
numNeighbors += 1 # Right neighbor is alive. | |
if cells[(left, below)] == ALIVE: | |
numNeighbors += 1 # Bottom-left neighbor is alive. | |
if cells[(x, below)] == ALIVE: | |
numNeighbors += 1 # Bottom neighbor is alive. | |
if cells[(right, below)] == ALIVE: | |
numNeighbors += 1 # Bottom-right neighbor is alive. | |
# Set cell based on Conway's Game of Life rules: | |
if cells[(x, y)] == ALIVE and (numNeighbors == 2 | |
or numNeighbors == 3): | |
# Living cells with 2 or 3 neighbors stay alive: | |
nextCells[(x, y)] = ALIVE | |
elif cells[(x, y)] == DEAD and numNeighbors == 3: | |
# Dead cells with 3 neighbors become alive: | |
nextCells[(x, y)] = ALIVE | |
else: | |
# Everything else dies or stays dead: | |
nextCells[(x, y)] = DEAD | |
try: | |
time.sleep(0.1) # Add a 1 second pause to reduce flickering. | |
except KeyboardInterrupt: | |
print('Conway\'s Game of Life') | |
print('By Al Sweigart [email protected]') | |
sys.exit() # When Ctrl-C is pressed, end the program. |
This file contains 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
"""Countdown, by Al Sweigart [email protected] | |
Show a countdown timer animation using a seven-segment display. | |
Press Ctrl-C to stop. | |
More info at https://en.wikipedia.org/wiki/Seven-segment_display | |
Requires sevseg.py to be in the same folder. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, artistic""" | |
__version__ = 0 | |
import sys, time | |
# (!) Change this to any number of seconds: | |
secondsLeft = 30 | |
def getSevSegStr(number, minWidth=0): | |
"""Return a seven-segment display string of number. The returned | |
string will be padded with zeros if it is smaller than minWidth.""" | |
# Convert number to string in case it's an int or float: | |
number = str(number).zfill(minWidth) | |
rows = ['', '', ''] | |
for i, numeral in enumerate(number): | |
if numeral == '.': # Render the decimal point. | |
rows[0] += ' ' | |
rows[1] += ' ' | |
rows[2] += '.' | |
continue # Skip the space in between digits. | |
elif numeral == '-': # Render the negative sign: | |
rows[0] += ' ' | |
rows[1] += ' __ ' | |
rows[2] += ' ' | |
elif numeral == '0': # Render the 0. | |
rows[0] += ' __ ' | |
rows[1] += '| |' | |
rows[2] += '|__|' | |
elif numeral == '1': # Render the 1. | |
rows[0] += ' ' | |
rows[1] += ' |' | |
rows[2] += ' |' | |
elif numeral == '2': # Render the 2. | |
rows[0] += ' __ ' | |
rows[1] += ' __|' | |
rows[2] += '|__ ' | |
elif numeral == '3': # Render the 3. | |
rows[0] += ' __ ' | |
rows[1] += ' __|' | |
rows[2] += ' __|' | |
elif numeral == '4': # Render the 4. | |
rows[0] += ' ' | |
rows[1] += '|__|' | |
rows[2] += ' |' | |
elif numeral == '5': # Render the 5. | |
rows[0] += ' __ ' | |
rows[1] += '|__ ' | |
rows[2] += ' __|' | |
elif numeral == '6': # Render the 6. | |
rows[0] += ' __ ' | |
rows[1] += '|__ ' | |
rows[2] += '|__|' | |
elif numeral == '7': # Render the 7. | |
rows[0] += ' __ ' | |
rows[1] += ' |' | |
rows[2] += ' |' | |
elif numeral == '8': # Render the 8. | |
rows[0] += ' __ ' | |
rows[1] += '|__|' | |
rows[2] += '|__|' | |
elif numeral == '9': # Render the 9. | |
rows[0] += ' __ ' | |
rows[1] += '|__|' | |
rows[2] += ' __|' | |
# Add a space (for the space in between numerals) if this | |
# isn't the last numeral and the decimal point isn't next: | |
if i != len(number) - 1 and number[i + 1] != '.': | |
rows[0] += ' ' | |
rows[1] += ' ' | |
rows[2] += ' ' | |
return '\n'.join(rows) | |
try: | |
while True: # Main program loop. | |
# Clear the screen by printing several newlines: | |
print('\n' * 60) | |
# Get the hours/minutes/seconds from secondsLeft: | |
# For example: 7265 is 2 hours, 1 minute, 5 seconds. | |
# So 7265 // 3600 is 2 hours: | |
hours = str(secondsLeft // 3600) | |
# And 7265 % 3600 is 65, and 65 // 60 is 1 minute: | |
minutes = str((secondsLeft % 3600) // 60) | |
# And 7265 % 60 is 5 seconds: | |
seconds = str(secondsLeft % 60) | |
# Get the digit strings from the sevseg module: | |
hDigits = getSevSegStr(hours, 2) | |
hTopRow, hMiddleRow, hBottomRow = hDigits.splitlines() | |
mDigits = getSevSegStr(minutes, 2) | |
mTopRow, mMiddleRow, mBottomRow = mDigits.splitlines() | |
sDigits = getSevSegStr(seconds, 2) | |
sTopRow, sMiddleRow, sBottomRow = sDigits.splitlines() | |
# Display the digits: | |
print(hTopRow + ' ' + mTopRow + ' ' + sTopRow) | |
print(hMiddleRow + ' * ' + mMiddleRow + ' * ' + sMiddleRow) | |
print(hBottomRow + ' * ' + mBottomRow + ' * ' + sBottomRow) | |
if secondsLeft == 0: | |
print() | |
print(' * * * * BOOM * * * *') | |
break | |
print() | |
print('Press Ctrl-C to quit.') | |
time.sleep(1) # Insert a one-second pause. | |
secondsLeft -= 1 | |
except KeyboardInterrupt: | |
print('Countdown, by Al Sweigart [email protected]') | |
sys.exit() # When Ctrl-C is pressed, end the program.) |
This file contains 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
"""Deep Cave, by Al Sweigart [email protected] | |
An animation of a deep cave that goes forever into the earth. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, beginner, scrolling, artistic""" | |
__version__ = 0 | |
import random, sys, time | |
# Set up the constants: | |
WIDTH = 70 # (!) Try changing this to 10 or 30. | |
PAUSE_AMOUNT = 0.05 # (!) Try changing this to 0 or 1.0. | |
print('Deep Cave, by Al Sweigart [email protected]') | |
print('Press Ctrl-C to stop.') | |
time.sleep(2) | |
leftWidth = 20 | |
gapWidth = 10 | |
while True: | |
# Display the tunnel segment: | |
rightWidth = WIDTH - gapWidth - leftWidth | |
print(('#' * leftWidth) + (' ' * gapWidth) + ('#' * rightWidth)) | |
# Check for Ctrl-C press during the brief pause: | |
try: | |
time.sleep(PAUSE_AMOUNT) | |
except KeyboardInterrupt: | |
sys.exit() # When Ctrl-C is pressed, end the program. | |
# Adjust the left side width: | |
diceRoll = random.randint(1, 6) | |
if diceRoll == 1 and leftWidth > 1: | |
leftWidth = leftWidth - 1 # Decrease left side width. | |
elif diceRoll == 2 and leftWidth + gapWidth < WIDTH - 1: | |
leftWidth = leftWidth + 1 # Increase left side width. | |
else: | |
pass # Do nothing; no change in left side width. | |
# Adjust the gap width: | |
# (!) Try uncommenting out all of the following code: | |
#diceRoll = random.randint(1, 6) | |
#if diceRoll == 1 and gapWidth > 1: | |
# gapWidth = gapWidth - 1 # Decrease gap width. | |
#elif diceRoll == 2 and leftWidth + gapWidth < WIDTH - 1: | |
# gapWidth = gapWidth + 1 # Increase gap width. | |
#else: | |
# pass # Do nothing; no change in gap width. |
This file contains 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
"""Diagonal Maze, by Al Sweigart [email protected] | |
Prints out a random, diagonal maze. (It is not a true | |
maze, but rather an artistic maze-like picture.) | |
Inspired by the "10 PRINT CHR$(205.5+RND(1)); : GOTO 10" program. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, beginner, artistic, maze""" | |
__version__ = 0 | |
import random | |
FORWARD_SLASH = chr(9585) # The ╱ character. | |
BACK_SLASH = chr(9586) # The ╲ character. | |
# (A list of chr() codes is at https://inventwithpython.com/charactermap) | |
for x in range(2000): # Loop for each slash in the current line. | |
# Randomly add a forward or back slash to the line. | |
if random.randint(0, 1) == 0: | |
print(FORWARD_SLASH, end='') | |
else: | |
print(BACK_SLASH, end='') |
This file contains 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
r"""Diamonds, by Al Sweigart [email protected] | |
Draws diamonds of various sizes. | |
/\ /\ | |
/ \ //\\ | |
/\ /\ / \ ///\\\ | |
/ \ //\\ / \ ////\\\\ | |
/\ /\ / \ ///\\\ \ / \\\\//// | |
/ \ //\\ \ / \\\/// \ / \\\/// | |
\ / \\// \ / \\// \ / \\// | |
\/ \/ \/ \/ \/ \/ | |
Tags: tiny, beginner, artistic""" | |
__version__ = 0 | |
def main(): | |
print('Diamonds, by Al Sweigart [email protected]') | |
# Display diamonds of sizes 0 through 6: | |
for diamondSize in range(0, 6): | |
displayOutlineDiamond(diamondSize) | |
print() # Print a newline. | |
displayFilledDiamond(diamondSize) | |
print() # Print a newline. | |
def displayOutlineDiamond(size): | |
# Display the top half of the diamond: | |
for i in range(size): | |
print(' ' * (size - i - 1), end='') # Left side space. | |
print('/', end='') # Left side of diamond. | |
print(' ' * (i * 2), end='') # Interior of diamond. | |
print('\\') # Right side of diamond. | |
# Display the bottom half of the diamond: | |
for i in range(size): | |
print(' ' * i, end='') # Left side space. | |
print('\\', end='') # Left side of diamond. | |
print(' ' * ((size - i - 1) * 2), end='') # Interior of diamond. | |
print('/') # Right side of diamond. | |
def displayFilledDiamond(size): | |
# Display the top half of the diamond: | |
for i in range(size): | |
print(' ' * (size - i - 1), end='') # Left side space. | |
print('/' * (i + 1), end='') # Left half of diamond. | |
print('\\' * (i + 1)) # Right half of diamond. | |
# Display the bottom half of the diamond: | |
for i in range(size): | |
print(' ' * i, end='') # Left side space. | |
print('\\' * (size - i), end='') # Left half of diamond. | |
print('/' * (size - i)) # Right half of diamond. | |
# If this program was run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
""" | |
Кубики та математика, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import random | |
import time | |
# Set up the constants: | |
DICE_WIDTH = 9 | |
DICE_HEIGHT = 5 | |
CANVAS_WIDTH = 79 | |
CANVAS_HEIGHT = 24 - 3 # -3 for room to enter the sum at the bottom. | |
# The duration is in seconds: | |
QUIZ_DURATION = 30 # (!) Try changing this to 10 or 60. | |
MIN_DICE = 2 # (!) Try changing this to 1 or 5. | |
MAX_DICE = 6 # (!) Try changing this to 14. | |
# (!) Try changing these to different numbers: | |
REWARD = 4 # (!) Points awarded for correct answers. | |
PENALTY = 1 # (!) Points removed for incorrect answers. | |
# (!) Try setting PENALTY to a negative number to give points for | |
# wrong answers! | |
# The program hangs if all of the dice can't fit on the screen: | |
assert MAX_DICE <= 14 | |
D1 = (['+-------+', | |
'| |', | |
'| O |', | |
'| |', | |
'+-------+'], 1) | |
D2a = (['+-------+', | |
'| O |', | |
'| |', | |
'| O |', | |
'+-------+'], 2) | |
D2b = (['+-------+', | |
'| O |', | |
'| |', | |
'| O |', | |
'+-------+'], 2) | |
D3a = (['+-------+', | |
'| O |', | |
'| O |', | |
'| O |', | |
'+-------+'], 3) | |
D3b = (['+-------+', | |
'| O |', | |
'| O |', | |
'| O |', | |
'+-------+'], 3) | |
D4 = (['+-------+', | |
'| O O |', | |
'| |', | |
'| O O |', | |
'+-------+'], 4) | |
D5 = (['+-------+', | |
'| O O |', | |
'| O |', | |
'| O O |', | |
'+-------+'], 5) | |
D6a = (['+-------+', | |
'| O O |', | |
'| O O |', | |
'| O O |', | |
'+-------+'], 6) | |
D6b = (['+-------+', | |
'| O O O |', | |
'| |', | |
'| O O O |', | |
'+-------+'], 6) | |
ALL_DICE = [D1, D2a, D2b, D3a, D3b, D4, D5, D6a, D6b] | |
print(''' | |
Додай всі числа на кубиках які є на екрані. У тебе є {} секунд, щоб | |
зробити якомога більше ігор. Ти отримаєш {} очків за кожну правильну відповідь | |
і втратиш {} очків за неправильну. | |
'''.format(QUIZ_DURATION, REWARD, PENALTY)) | |
input('Тисни Enter щоб почати...') | |
# Keep track of how many answers were correct and incorrect: | |
correctAnswers = 0 | |
incorrectAnswers = 0 | |
startTime = time.time() | |
while time.time() < startTime + QUIZ_DURATION: # Main game loop. | |
# Come up with the dice to display: | |
sumAnswer = 0 | |
diceFaces = [] | |
for i in range(random.randint(MIN_DICE, MAX_DICE)): | |
die = random.choice(ALL_DICE) | |
# die[0] contains the list of strings of the die face: | |
diceFaces.append(die[0]) | |
# die[1] contains the integer number of pips on the face: | |
sumAnswer += die[1] | |
# Contains (x, y) tuples of the top-left corner of each die. | |
topLeftDiceCorners = [] | |
# Figure out where dice should go: | |
for i in range(len(diceFaces)): | |
while True: | |
# Find a random place on the canvas to put the die: | |
left = random.randint(0, CANVAS_WIDTH - 1 - DICE_WIDTH) | |
top = random.randint(0, CANVAS_HEIGHT - 1 - DICE_HEIGHT) | |
# Get the x, y coordinates for all four corners: | |
# left | |
# v | |
#top > +-------+ ^ | |
# | O | | | |
# | O | DICE_HEIGHT (5) | |
# | O | | | |
# +-------+ v | |
# <-------> | |
# DICE_WIDTH (9) | |
topLeftX = left | |
topLeftY = top | |
topRightX = left + DICE_WIDTH | |
topRightY = top | |
bottomLeftX = left | |
bottomLeftY = top + DICE_HEIGHT | |
bottomRightX = left + DICE_WIDTH | |
bottomRightY = top + DICE_HEIGHT | |
# Check if this die overlaps with previous dice. | |
overlaps = False | |
for prevDieLeft, prevDieTop in topLeftDiceCorners: | |
prevDieRight = prevDieLeft + DICE_WIDTH | |
prevDieBottom = prevDieTop + DICE_HEIGHT | |
# Check each corner of this die to see if it is inside | |
# of the area the previous die: | |
for cornerX, cornerY in ((topLeftX, topLeftY), | |
(topRightX, topRightY), | |
(bottomLeftX, bottomLeftY), | |
(bottomRightX, bottomRightY)): | |
if (prevDieLeft <= cornerX < prevDieRight | |
and prevDieTop <= cornerY < prevDieBottom): | |
overlaps = True | |
if not overlaps: | |
# It doesn't overlap, so we can put it here: | |
topLeftDiceCorners.append((left, top)) | |
break | |
# Draw the dice on the canvas: | |
# Keys are (x, y) tuples of ints, values the character at that | |
# position on the canvas: | |
canvas = {} | |
# Loop over each die: | |
for i, (dieLeft, dieTop) in enumerate(topLeftDiceCorners): | |
# Loop over each character in the die's face: | |
dieFace = diceFaces[i] | |
for dx in range(DICE_WIDTH): | |
for dy in range(DICE_HEIGHT): | |
# Copy this character to the correct place on the canvas: | |
canvasX = dieLeft + dx | |
canvasY = dieTop + dy | |
# Note that in dieFace, a list of strings, the x and y | |
# are swapped: | |
canvas[(canvasX, canvasY)] = dieFace[dy][dx] | |
# Display the canvas on the screen: | |
for cy in range(CANVAS_HEIGHT): | |
for cx in range(CANVAS_WIDTH): | |
print(canvas.get((cx, cy), ' '), end='') | |
print() # Print a newline. | |
# Let the player enter their answer: | |
response = input('Введи суму: ').strip() | |
if response.isdecimal() and int(response) == sumAnswer: | |
correctAnswers += 1 | |
else: | |
print('Невірно, сума дорівнює', sumAnswer) | |
time.sleep(2) | |
incorrectAnswers += 1 | |
# Display the final score: | |
score = (correctAnswers * REWARD) - (incorrectAnswers * PENALTY) | |
print('Вірно: ', correctAnswers) | |
print('Невірно: ', incorrectAnswers) | |
print('Рахунок: ', score) |
This file contains 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
""" | |
Цифровий годинник, Al Sweigart [email protected], адаптував Данило (danbst) | |
""" | |
import sys, time | |
def getSevSegStr(number, padcount): | |
digits = [ | |
' _ .| |.|_|', | |
' . |. |', | |
' _ . _|.|_ ', | |
' _ . _|. _|', | |
' .|_|. |', | |
' _ .|_ . _|', | |
' _ .|_ .|_|', | |
' _ . |. |', | |
' _ .|_|.|_|', | |
' _ .|_|. _|', | |
] | |
num_d = [ ord(x) - ord('0') for x in str(number).ljust(padcount,'0')] | |
num_s = [ digits[d].split('.') for d in num_d ] | |
num_real = [ ''.join(row) for row in zip(*num_s) ] | |
return '\n'.join(num_real) | |
try: | |
while True: # Main program loop. | |
# Clear the screen by printing several newlines: | |
print('\n' * 60) | |
# Get the current time from the computer's clock: | |
currentTime = time.localtime() | |
# % 12 so we use a 12-hour clock, not 24: | |
hours = str(currentTime.tm_hour % 12) | |
if hours == '0': | |
hours = '12' # 12-hour clocks show 12:00, not 00:00. | |
minutes = str(currentTime.tm_min) | |
seconds = str(currentTime.tm_sec) | |
# Get the digit strings from the sevseg module: | |
hDigits = getSevSegStr(hours, 2) | |
hTopRow, hMiddleRow, hBottomRow = hDigits.splitlines() | |
mDigits = getSevSegStr(minutes, 2) | |
mTopRow, mMiddleRow, mBottomRow = mDigits.splitlines() | |
sDigits = getSevSegStr(seconds, 2) | |
sTopRow, sMiddleRow, sBottomRow = sDigits.splitlines() | |
# Display the digits: | |
print(hTopRow + ' ' + mTopRow + ' ' + sTopRow) | |
print(hMiddleRow + ' * ' + mMiddleRow + ' * ' + sMiddleRow) | |
print(hBottomRow + ' * ' + mBottomRow + ' * ' + sBottomRow) | |
print() | |
print('Press Ctrl-C to quit.') | |
# Keep looping until the second changes: | |
while True: | |
time.sleep(0.01) | |
if time.localtime().tm_sec != currentTime.tm_sec: | |
break | |
except KeyboardInterrupt: | |
print('Digital Clock, by Al Sweigart [email protected]') | |
sys.exit() # When Ctrl-C is pressed, end the program. |
This file contains 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
"""Digital Stream, by Al Sweigart [email protected] | |
A screensaver in the style of The Matrix movie's visuals. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, artistic, beginner, scrolling""" | |
__version__ = 0 | |
import random, shutil, sys, time | |
# Set up the constants: | |
MIN_STREAM_LENGTH = 6 # (!) Try changing this to 1 or 50. | |
MAX_STREAM_LENGTH = 14 # (!) Try changing this to 100. | |
PAUSE = 0.1 # (!) Try changing this to 0.0 or 2.0. | |
STREAM_CHARS = ['0', '1'] # (!) Try changing this other characters. | |
# Density can range from 0.0 to 1.0: | |
DENSITY = 0.02 # (!) Try changing this to 0.10 or 0.30. | |
# Get the size of the terminal window: | |
WIDTH = shutil.get_terminal_size()[0] | |
# We can't print to the last column on Windows without it adding a | |
# newline automatically, so reduce the width by one: | |
WIDTH -= 1 | |
print('Digital Stream Screensaver, by Al Sweigart [email protected]') | |
print('Press Ctrl-C to quit.') | |
time.sleep(2) | |
try: | |
# For each column, when the counter is 0, no stream is shown. | |
# Otherwise, it acts as a counter for how many times a 1 or 0 | |
# should be displayed in that column. | |
columns = [0] * WIDTH | |
while True: | |
# Set up the counter for each column: | |
for i in range(WIDTH): | |
if columns[i] == 0: | |
if random.random() <= DENSITY: | |
# Restart a stream on this column. | |
columns[i] = random.randint(MIN_STREAM_LENGTH, | |
MAX_STREAM_LENGTH) | |
# Display an empty space or a 1/0 character. | |
if columns[i] > 0: | |
print(random.choice(STREAM_CHARS), end='') | |
columns[i] -= 1 | |
else: | |
print(' ', end='') | |
print() # Print a newline at the end of the row of columns. | |
sys.stdout.flush() # Make sure text appears on the screen. | |
time.sleep(PAUSE) | |
except KeyboardInterrupt: | |
sys.exit() # When Ctrl-C is pressed, end the program. |
This file contains 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
"""DNA, by Al Sweigart [email protected] | |
A simple animation of a DNA double-helix. Press Ctrl-C to stop. | |
Inspired by matoken https://asciinema.org/a/155441 | |
This and other games are available at https://nostarch.com/XX | |
Tags: short, artistic, scrolling, science""" | |
__version__ = 0 | |
import random, sys, time | |
PAUSE = 0.15 # (!) Try changing this to 0.5 or 0.0. | |
# These are the individual rows of the DNA animation: | |
ROWS = [ | |
#123456789 <- Use this to measure the number of spaces: | |
' ##', # Index 0 has no {}. | |
' #{}-{}#', | |
' #{}---{}#', | |
' #{}-----{}#', | |
' #{}------{}#', | |
' #{}------{}#', | |
' #{}-----{}#', | |
' #{}---{}#', | |
' #{}-{}#', | |
' ##', # Index 9 has no {}. | |
' #{}-{}#', | |
' #{}---{}#', | |
' #{}-----{}#', | |
' #{}------{}#', | |
' #{}------{}#', | |
' #{}-----{}#', | |
' #{}---{}#', | |
' #{}-{}#'] | |
#123456789 <- Use this to measure the number of spaces: | |
try: | |
print('DNA Animation, by Al Sweigart [email protected]') | |
print('Press Ctrl-C to quit...') | |
time.sleep(2) | |
rowIndex = 0 | |
while True: # Main program loop. | |
# Increment rowIndex to draw next row: | |
rowIndex = rowIndex + 1 | |
if rowIndex == len(ROWS): | |
rowIndex = 0 | |
# Row indexes 0 and 9 don't have nucleotides: | |
if rowIndex == 0 or rowIndex == 9: | |
print(ROWS[rowIndex]) | |
continue | |
# Select random nucleotide pairs, guanine-cytosine and | |
# adenine-thymine: | |
randomSelection = random.randint(1, 4) | |
if randomSelection == 1: | |
leftNucleotide, rightNucleotide = 'A', 'T' | |
elif randomSelection == 2: | |
leftNucleotide, rightNucleotide = 'T', 'A' | |
elif randomSelection == 3: | |
leftNucleotide, rightNucleotide = 'C', 'G' | |
elif randomSelection == 4: | |
leftNucleotide, rightNucleotide = 'G', 'C' | |
# Print the row. | |
print(ROWS[rowIndex].format(leftNucleotide, rightNucleotide)) | |
time.sleep(PAUSE) # Add a slight pause. | |
except KeyboardInterrupt: | |
sys.exit() # When Ctrl-C is pressed, end the program. |
This file contains 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
if True: | |
import turtle | |
turtle.speed(0) | |
turtle.clear() | |
def dragon(level): | |
if level == 1: | |
return [90] | |
else: | |
previous = dragon(level - 1) | |
return previous + [90] + [-x for x in previous][::-1] | |
for action in dragon(15): | |
turtle.forward(3) | |
turtle.left(action) |
This file contains 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
"""Etching Drawer, by Al Sweigart [email protected] | |
An art program that draws a continuous line around the screen using the | |
WASD keys. Inspired by Etch A Sketch toys. | |
For example, you can draw Hilbert Curve fractal with: | |
SDWDDSASDSAAWASSDSASSDWDSDWWAWDDDSASSDWDSDWWAWDWWASAAWDWAWDDSDW | |
Or an even larger Hilbert Curve fractal with: | |
DDSAASSDDWDDSDDWWAAWDDDDSDDWDDDDSAASDDSAAAAWAASSSDDWDDDDSAASDDSAAAAWA | |
ASAAAAWDDWWAASAAWAASSDDSAASSDDWDDDDSAASDDSAAAAWAASSDDSAASSDDWDDSDDWWA | |
AWDDDDDDSAASSDDWDDSDDWWAAWDDWWAASAAAAWDDWAAWDDDDSDDWDDSDDWDDDDSAASDDS | |
AAAAWAASSDDSAASSDDWDDSDDWWAAWDDDDDDSAASSDDWDDSDDWWAAWDDWWAASAAAAWDDWA | |
AWDDDDSDDWWAAWDDWWAASAAWAASSDDSAAAAWAASAAAAWDDWAAWDDDDSDDWWWAASAAAAWD | |
DWAAWDDDDSDDWDDDDSAASSDDWDDSDDWWAAWDD | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, artistic""" | |
__version__ = 0 | |
import shutil, sys | |
# Set up the constants for line characters: | |
UP_DOWN_CHAR = chr(9474) # Character 9474 is '│' | |
LEFT_RIGHT_CHAR = chr(9472) # Character 9472 is '─' | |
DOWN_RIGHT_CHAR = chr(9484) # Character 9484 is '┌' | |
DOWN_LEFT_CHAR = chr(9488) # Character 9488 is '┐' | |
UP_RIGHT_CHAR = chr(9492) # Character 9492 is '└' | |
UP_LEFT_CHAR = chr(9496) # Character 9496 is '┘' | |
UP_DOWN_RIGHT_CHAR = chr(9500) # Character 9500 is '├' | |
UP_DOWN_LEFT_CHAR = chr(9508) # Character 9508 is '┤' | |
DOWN_LEFT_RIGHT_CHAR = chr(9516) # Character 9516 is '┬' | |
UP_LEFT_RIGHT_CHAR = chr(9524) # Character 9524 is '┴' | |
CROSS_CHAR = chr(9532) # Character 9532 is '┼' | |
# A list of chr() codes is at https://inventwithpython.com/chr | |
# Get the size of the terminal window: | |
CANVAS_WIDTH, CANVAS_HEIGHT = shutil.get_terminal_size() | |
# We can't print to the last column on Windows without it adding a | |
# newline automatically, so reduce the width by one: | |
CANVAS_WIDTH -= 1 | |
# Leave room at the bottom few rows for the command info lines. | |
CANVAS_HEIGHT -= 5 | |
"""The keys for canvas will be (x, y) integer tuples for the coordinate, | |
and the value is a set of letters W, A, S, D that tell what kind of line | |
should be drawn.""" | |
canvas = {} | |
cursorX = 0 | |
cursorY = 0 | |
def getCanvasString(canvasData, cx, cy): | |
"""Returns a multiline string of the line drawn in canvasData.""" | |
canvasStr = '' | |
"""canvasData is a dictionary with (x, y) tuple keys and values that | |
are sets of 'W', 'A', 'S', and/or 'D' strings to show which | |
directions the lines are drawn at each xy point.""" | |
for rowNum in range(CANVAS_HEIGHT): | |
for columnNum in range(CANVAS_WIDTH): | |
if columnNum == cx and rowNum == cy: | |
canvasStr += '#' | |
continue | |
# Add the line character for this point to canvasStr. | |
cell = canvasData.get((columnNum, rowNum)) | |
if cell in (set(['W', 'S']), set(['W']), set(['S'])): | |
canvasStr += UP_DOWN_CHAR | |
elif cell in (set(['A', 'D']), set(['A']), set(['D'])): | |
canvasStr += LEFT_RIGHT_CHAR | |
elif cell == set(['S', 'D']): | |
canvasStr += DOWN_RIGHT_CHAR | |
elif cell == set(['A', 'S']): | |
canvasStr += DOWN_LEFT_CHAR | |
elif cell == set(['W', 'D']): | |
canvasStr += UP_RIGHT_CHAR | |
elif cell == set(['W', 'A']): | |
canvasStr += UP_LEFT_CHAR | |
elif cell == set(['W', 'S', 'D']): | |
canvasStr += UP_DOWN_RIGHT_CHAR | |
elif cell == set(['W', 'S', 'A']): | |
canvasStr += UP_DOWN_LEFT_CHAR | |
elif cell == set(['A', 'S', 'D']): | |
canvasStr += DOWN_LEFT_RIGHT_CHAR | |
elif cell == set(['W', 'A', 'D']): | |
canvasStr += UP_LEFT_RIGHT_CHAR | |
elif cell == set(['W', 'A', 'S', 'D']): | |
canvasStr += CROSS_CHAR | |
elif cell == None: | |
canvasStr += ' ' | |
canvasStr += '\n' # Add a newline at the end of each row. | |
return canvasStr | |
moves = [] | |
while True: # Main program loop. | |
# Draw the lines based on the data in canvas: | |
print(getCanvasString(canvas, cursorX, cursorY)) | |
print('WASD keys to move, H for help, C to clear, ' | |
+ 'F to save, or QUIT.') | |
response = input('> ').upper() | |
if response == 'QUIT': | |
print('Thanks for playing!') | |
sys.exit() # Quit the program. | |
elif response == 'H': | |
print('Enter W, A, S, and D characters to move the cursor and') | |
print('draw a line behind it as it moves. For example, ddd') | |
print('draws a line going right and sssdddwwwaaa draws a box.') | |
print() | |
print('You can save your drawing to a text file by entering F.') | |
input('Press Enter to return to the program...') | |
continue | |
elif response == 'C': | |
canvas = {} # Erase the canvas data. | |
moves.append('C') # Record this move. | |
elif response == 'F': | |
# Save the canvas string to a text file: | |
try: | |
print('Enter filename to save to:') | |
filename = input('> ') | |
# Make sure the filename ends with .txt: | |
if not filename.endswith('.txt'): | |
filename += '.txt' | |
with open(filename, 'w', encoding='utf-8') as file: | |
file.write(''.join(moves) + '\n') | |
file.write(getCanvasString(canvas, None, None)) | |
except: | |
print('ERROR: Could not save file.') | |
for command in response: | |
if command not in ('W', 'A', 'S', 'D'): | |
continue # Ignore this letter and continue to the next one. | |
moves.append(command) # Record this move. | |
# The first line we add needs to form a full line: | |
if canvas == {}: | |
if command in ('W', 'S'): | |
# Make the first line a horizontal one: | |
canvas[(cursorX, cursorY)] = set(['W', 'S']) | |
elif command in ('A', 'D'): | |
# Make the first line a vertical one: | |
canvas[(cursorX, cursorY)] = set(['A', 'D']) | |
# Update x and y: | |
if command == 'W' and cursorY > 0: | |
canvas[(cursorX, cursorY)].add(command) | |
cursorY = cursorY - 1 | |
elif command == 'S' and cursorY < CANVAS_HEIGHT - 1: | |
canvas[(cursorX, cursorY)].add(command) | |
cursorY = cursorY + 1 | |
elif command == 'A' and cursorX > 0: | |
canvas[(cursorX, cursorY)].add(command) | |
cursorX = cursorX - 1 | |
elif command == 'D' and cursorX < CANVAS_WIDTH - 1: | |
canvas[(cursorX, cursorY)].add(command) | |
cursorX = cursorX + 1 | |
else: | |
# If the cursor doesn't move because it would have moved off | |
# the edge of the canvas, then don't change the set at | |
# canvas[(cursorX, cursorY)]. | |
continue | |
# If there's no set for (cursorX, cursorY), add an empty set: | |
if (cursorX, cursorY) not in canvas: | |
canvas[(cursorX, cursorY)] = set() | |
# Add the direction string to this xy point's set: | |
if command == 'W': | |
canvas[(cursorX, cursorY)].add('S') | |
elif command == 'S': | |
canvas[(cursorX, cursorY)].add('W') | |
elif command == 'A': | |
canvas[(cursorX, cursorY)].add('D') | |
elif command == 'D': | |
canvas[(cursorX, cursorY)].add('A') |
This file contains 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
""" | |
Ковбой, Al Sweigart [email protected], переклав Данило (danbst) | |
""" | |
import random, sys, time | |
print(''' | |
Найшвидший ковбой | |
Час перевірити твої рефлекси, і побачити хто найшвидший ковбой | |
на всьому Дикому Заході! | |
Коли ти побачиш "ТЯГНИ!", у тебе є 0.3 секунди щоб натиснути Enter. | |
Якщо ти будеш повільний, то програєш. Але якщо натиснеш наперед -- також програєш. | |
Тисни Enter для початку... | |
''') | |
input() | |
while True: | |
print() | |
print('Жарке полуденне сонце освічувало майже порожню вулицю...') | |
time.sleep(random.randint(20, 50) / 10.0) | |
print('ТЯГНИ!') | |
drawTime = time.time() | |
input() # This function call doesn't return until Enter is pressed. | |
timeElapsed = time.time() - drawTime | |
if timeElapsed < 0.01: | |
# If the player pressed Enter before DRAW! appeared, the input() | |
# call returns almost instantly. | |
print('Ти витягнув зарано, ти програв!') | |
elif timeElapsed > 0.3: | |
timeElapsed = round(timeElapsed, 4) | |
print('Ти витягнув за', timeElapsed, 'секунд. Повільний як черепаха!') | |
else: | |
timeElapsed = round(timeElapsed, 4) | |
print('Ти витягнув за', timeElapsed, 'секунд.') | |
print('Ти найшвидший ковбой на всьому Дикому Заході! Молодець!') | |
print('Введи QUIT щоб вийти, або натисни Enter щоб зіграти ще раз') | |
response = input('> ').upper() | |
if response == 'QUIT': | |
print('Дякую за гру!') | |
sys.exit() |
This file contains 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
"""Fireflies, by Al Sweigart [email protected] | |
A beautiful animation of fireflies. Press Ctrl-C to stop. | |
This program MUST be run in a Terminal/Command Prompt window. | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, artistic, bext""" | |
__version__ = 0 | |
import math, time, sys, os, random | |
# This program draws 3D points that rotate around a center point. This | |
# gives the fireflies a "swirling" kind of movement. | |
# Set up the constants: | |
PAUSE_AMOUNT = 0.15 # (!) Try changing this to 0.05 or 0.5. | |
NUMBER_OF_FIREFLIES = 16 # (!) Try changing this to 2 or 100. | |
LIT_DURATION = 3 # (!) Try changing this to 100. | |
MIN_LIT_FREQ = 10 | |
MAX_LIT_FREQ = 40 | |
WIDTH, HEIGHT = 80, 24 # Width & height of the swarm, in text cells. | |
SCALEX = (WIDTH - 4) // 4 | |
SCALEY = (HEIGHT - 4) // 4 | |
# Text cells are twice as tall as they are wide, so adjust SCALEY: | |
SCALEY = SCALEY * 2 | |
TRANSLATEX = (WIDTH - 4) // 2 # Put the swarm in the screen's center. | |
TRANSLATEY = (HEIGHT - 4) // 2 | |
FIREFLY_DARK = '.' # Draw a period for normal fireflies. | |
FIREFLY_LIGHT = chr(9604) # Draw a block for lit up fireflies. | |
# Several of the data structures are lists/tuples with x, y, z at | |
# indexes 0, 1, and 2 respectively. We'll use constants for them: | |
X, Y, Z = 0, 1, 2 | |
def main(): | |
# First we create data structures for our fireflies. | |
# Each firefly is represented by dictionary with keys | |
# 'originalPosition', 'rotationAmount', 'rotVelocity', | |
# 'timeToLit', 'isLit'. | |
fireflies = [] | |
for i in range(NUMBER_OF_FIREFLIES): | |
firefly = {} # The dictionary for a new, single firefly. | |
# Create the original XYZ positions of the firefly. (Really, | |
# they're just random points on a sphere that rotate around.) | |
# Create points on a sphere from random latitude and longitude: | |
latitude = math.acos(2 * random.random() - 1) - (math.pi / 2) | |
longitude = 2 * math.pi * random.random() | |
# Convert the latitude and longitude to an xyz point: | |
x = math.cos(latitude) * math.cos(longitude) | |
y = math.cos(latitude) * math.sin(longitude) | |
z = math.sin(latitude) | |
firefly['originalPosition'] = (x, y, z) | |
# Firefly positions start with no rotation: | |
firefly['rotAmounts'] = [0, 0, 0] # x, y, and z rotation. | |
# Randomly choose rotation velocity for each axis: | |
firefly['rotVelocity'] = [ | |
random.randint(-100, 100) / 1000.0, | |
random.randint(-100, 100) / 1000.0, | |
random.randint(-100, 100) / 1000.0, | |
] | |
# Holds time until the firefly changes between light/dark: | |
firefly['timeToLit'] = random.randint(MIN_LIT_FREQ, MAX_LIT_FREQ) | |
# Fireflies start off dark: | |
firefly['isLit'] = False | |
# Append this dictionary to the list of firefly dictionaries: | |
fireflies.append(firefly) | |
# Next, we animate and display the fireflies on the screen: | |
while True: # Main program loop. | |
# lightPoints is a list of (x, y) tuples for where lit up | |
# fireflies should be displayed on the screen. darkPoints is | |
# for dark fireflies. | |
lightPoints = [] | |
darkPoints = [] | |
# Move the fireflies and figure out where to display them: | |
for firefly in fireflies: | |
# Change the rotation amount by the rotation velocity: | |
firefly['rotAmounts'][X] += firefly['rotVelocity'][X] | |
firefly['rotAmounts'][Y] += firefly['rotVelocity'][Y] | |
firefly['rotAmounts'][Z] += firefly['rotVelocity'][Z] | |
# To avoid rounding errors from accumulating, we recalculate | |
# the rotated position by rotating the original position | |
# instead of the firefly's last position. | |
# For example, if the firefly rotates 5 degrees, and then 6 | |
# more degrees, we calculate it's rotation 5 degrees from | |
# it's original position, and then 11 degrees from its | |
# original position. | |
rotatedPoint = rotatePoint( | |
firefly['originalPosition'][X], | |
firefly['originalPosition'][Y], | |
firefly['originalPosition'][Z], | |
firefly['rotAmounts'][X], | |
firefly['rotAmounts'][Y], | |
firefly['rotAmounts'][Z], | |
) | |
rotatedAndTransformedPoint = transformPoint(rotatedPoint) | |
# Determine if the firelies are light or dark: | |
firefly['timeToLit'] -= 1 # Decrease this each iteration. | |
if firefly['timeToLit'] <= 0: | |
if firefly['isLit']: | |
# Firefly will be dark for a random amount of time: | |
firefly['timeToLit'] = random.randint(MIN_LIT_FREQ, | |
MAX_LIT_FREQ) | |
else: | |
# Firefly will be light for a set amount of time: | |
firefly['timeToLit'] = LIT_DURATION | |
# Toggle isLit to the opposite value: | |
firefly['isLit'] = not firefly['isLit'] | |
# Determine which character to draw on the screen: | |
if firefly['isLit']: | |
lightPoints.append(rotatedAndTransformedPoint) | |
else: | |
darkPoints.append(rotatedAndTransformedPoint) | |
# Display the animate firefly scene: | |
displayFireflies(lightPoints, darkPoints) | |
time.sleep(PAUSE_AMOUNT) # Pause before erasing the screen. | |
clearScreen() | |
def rotatePoint(x, y, z, ax, ay, az): | |
"""Returns a 3D point that is rotated from the x, y, z point | |
arguments. This new point is rotated around the (0, 0, 0) origin | |
by angles ax, ay, az (in radians). | |
Directions of each axis: | |
-y | |
| | |
+-- +x | |
/ | |
+z | |
""" | |
# Rotate around x axis: | |
rotatedX = x | |
rotatedY = (y * math.cos(ax)) - (z * math.sin(ax)) | |
rotatedZ = (y * math.sin(ax)) + (z * math.cos(ax)) | |
x, y, z = rotatedX, rotatedY, rotatedZ | |
# Rotate around y axis: | |
rotatedX = (z * math.sin(ay)) + (x * math.cos(ay)) | |
rotatedY = y | |
rotatedZ = (z * math.cos(ay)) - (x * math.sin(ay)) | |
x, y, z = rotatedX, rotatedY, rotatedZ | |
# Rotate around z axis: | |
rotatedX = (x * math.cos(az)) - (y * math.sin(az)) | |
rotatedY = (x * math.sin(az)) + (y * math.cos(az)) | |
rotatedZ = z | |
return (rotatedX, rotatedY, rotatedZ) | |
def transformPoint(point): | |
"""Resizes this 2D point by a scale of scalex and scaley, then moves | |
the point by translatex and translatey.""" | |
return (int(point[X] * SCALEX + TRANSLATEX), | |
int(point[Y] * SCALEY + TRANSLATEY)) | |
def displayFireflies(lightPoints, darkPoints): | |
# Loop over every place on the screen and draw fireflies: | |
for y in range(HEIGHT): | |
for x in range(WIDTH): | |
if (x, y) in lightPoints: | |
print(FIREFLY_LIGHT, end='') # Display lit firefly. | |
elif (x, y) in darkPoints: | |
print(FIREFLY_DARK, end='') # Display dark firefly. | |
else: | |
print(' ', end='') # Display an empty space. | |
print() # Print a newline. | |
print('Press Ctrl-C to quit.', end='', flush=True) | |
def clearScreen(): | |
"""Clear the terminal window screen by running cls or clear.""" | |
if sys.platform == 'win32': | |
os.system('cls') # Windows uses the cls command. | |
else: | |
os.system('clear') # macOS and Linux use the clear command. | |
# If this program was run (instead of imported), run the game: | |
if __name__ == '__main__': | |
try: | |
main() | |
except KeyboardInterrupt: | |
print('Fireflies, by Al Sweigart [email protected]') | |
sys.exit() # When Ctrl-C is pressed, end the program. |
This file contains 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
"""Flood It (Letter Version), by Al Sweigart [email protected] | |
A colorful game where you try to fill the board with a single color. | |
(This version uses letters instead of colors for colorblind users.) | |
This and other games are available at https://nostarch.com/XX | |
Tags: short, game""" | |
__version__ = 0 | |
import random, sys | |
# Set up the constants: | |
WIDTH = 16 | |
HEIGHT = 14 | |
# All the letters used on the board: | |
LETTERS = ('s', 'o', 'x', 'm', 'a', 'i') | |
def main(): | |
print('''Flood It (Letter Version) | |
By Al Sweigart [email protected] | |
Set the letter of the upper left square, which fills in all the | |
adjacent squares of that letter. Try to make the entire board the | |
same letter.''') | |
gameBoard = getNewBoard() | |
movesLeft = 20 | |
while True: # Main game loop. | |
displayBoard(gameBoard) | |
print('Moves left:', movesLeft) | |
playerMove = askForPlayerMove() | |
changeTile(playerMove, gameBoard, 0, 0) | |
movesLeft -= 1 | |
if hasWon(gameBoard): | |
displayBoard(gameBoard) | |
print('You have won!') | |
break | |
elif movesLeft == 0: | |
displayBoard(gameBoard) | |
print('You have run out of moves!') | |
break | |
def getNewBoard(): | |
"""Return a dictionary of a new Flood It board.""" | |
board = {} | |
# Create random letters for the board. | |
for x in range(WIDTH): | |
for y in range(HEIGHT): | |
board[(x, y)] = random.choice(LETTERS) | |
# Make several tiles the same letter as their neighbor. | |
for i in range(WIDTH * HEIGHT): | |
x = random.randint(0, WIDTH - 2) | |
y = random.randint(0, HEIGHT - 2) | |
if random.randint(0, 1) == 0: | |
board[(x + 1, y)] = board[(x, y)] | |
else: | |
board[(x, y + 1)] = board[(x, y)] | |
return board | |
def displayBoard(board): | |
"""Display the board on the screen.""" | |
# Print first row with '>'. | |
print(' >', end='') | |
for x in range(WIDTH): | |
print(board[(x, 0)], end='') | |
print() | |
# Print each row after the first. | |
for y in range(1, HEIGHT): | |
print(' ', end='') | |
for x in range(WIDTH): | |
print(board[(x, y)], end='') | |
print() | |
def askForPlayerMove(): | |
"""Let the player select a letter to paint the upper left tile.""" | |
while True: | |
print('Choose one of s o x m a i or QUIT.') | |
move = input('> ').lower() | |
if move == 'quit': | |
sys.exit() | |
if move in LETTERS: | |
return move | |
def changeTile(move, board, x, y, charToChange=None): | |
"""Change the letter of a tile.""" | |
if x == 0 and y == 0: | |
charToChange = board[(x, y)] | |
if move == charToChange: | |
return # Already is the same letter. | |
board[(x, y)] = move | |
if x > 0 and board[(x - 1, y)] == charToChange: | |
changeTile(move, board, x - 1, y, charToChange) | |
if y > 0 and board[(x, y - 1)] == charToChange: | |
changeTile(move, board, x, y - 1, charToChange) | |
if x < WIDTH - 1 and board[(x + 1, y)] == charToChange: | |
changeTile(move, board, x + 1, y, charToChange) | |
if y < HEIGHT - 1 and board[(x, y + 1)] == charToChange: | |
changeTile(move, board, x, y + 1, charToChange) | |
def hasWon(board): | |
"""Return True if the entire board is one letter.""" | |
tile = board[(0, 0)] | |
for x in range(WIDTH): | |
for y in range(HEIGHT): | |
if board[(x, y)] != tile: | |
return False | |
return True | |
# If this program was run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
"""Four in a Row, by Al Sweigart [email protected] | |
A tile-dropping game to get four in a row, similar to Connect Four. | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, game, board game, two-player""" | |
__version__ = 0 | |
import sys | |
# 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 displayBoard() & 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 | |
def main(): | |
print("""Four in a Row, by Al Sweigart [email protected] | |
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 = askForPlayerMove(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 ' + playerTurn + ' has won!') | |
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 columnIndex in range(BOARD_WIDTH): | |
for rowIndex in range(BOARD_HEIGHT): | |
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(""" | |
1234567 | |
+-------+ | |
|{}{}{}{}{}{}{}| | |
|{}{}{}{}{}{}{}| | |
|{}{}{}{}{}{}{}| | |
|{}{}{}{}{}{}{}| | |
|{}{}{}{}{}{}{}| | |
|{}{}{}{}{}{}{}| | |
+-------+""".format(*tileChars)) | |
def askForPlayerMove(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('Player {}, enter a column or QUIT:'.format(playerTile)) | |
response = input('> ').upper().strip() | |
if response == 'QUIT': | |
print('Thanks for playing!') | |
sys.exit() | |
if response not in COLUMN_LABELS: | |
print('Enter a number from 1 to {}.'.format(BOARD_WIDTH)) | |
continue # Ask player again for their move. | |
columnIndex = int(response) - 1 # -1 for 0-based the index. | |
# 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 row on `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 horizontal four-in-a-row going 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 vertical 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 the program is run (instead of imported), run the game: | |
if __name__ == '__main__': | |
main() |
This file contains 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
"""Nonuniform Fractal Tree Drawer, by Al Sweigart [email protected] | |
Draws nonuniform fractal trees with turtle graphics.""" | |
__version__ = 0 | |
import turtle | |
import random | |
import time | |
PALE_TAN = '#FEF9EE' | |
DARK_BROWN = '#130d0f' | |
turtle.bgcolor(PALE_TAN) | |
turtle.pencolor(DARK_BROWN) | |
turtle.tracer(10000, 0) # Make the turtle draw faster. | |
def main(): | |
seed = 0 | |
while True: | |
# Get psuedorandom numbers for the branch properties: | |
random.seed(seed) | |
drawTree(0, -310, 90, seed) | |
time.sleep(2) | |
turtle.clear() | |
seed += 1 | |
turtle.update() # Finish drawing the screen. | |
turtle.exitonclick() # When user clicks on the window, close it. | |
def drawBranch(x, y, direction, branchLength): | |
# if the branch is too small, just quit | |
if branchLength < 5: | |
return | |
# Draw the branch: | |
branchThickness = max(branchLength / 7.0, 1) + random.randint(-1, 1) | |
turtle.pensize(branchThickness) | |
for i in range(4): | |
turtle.forward(branchLength / 4.0 + random.randint(-10, 10)) | |
turtle.left(random.randint(-8, 8)) | |
if random.randint(0, 5) == 0: | |
tinyBranchAngle = random.randint(-LEFT_ANGLE, RIGHT_ANGLE) | |
turtle.right(tinyBranchAngle) | |
drawBranch(turtle.xcor(), turtle.ycor(), turtle.heading(), branchLength / 2) | |
turtle.left(tinyBranchAngle) | |
turtle.pensize(branchThickness) | |
if random.randint(0, 5) == 0: | |
branchLength = branchLength * 0.9 | |
# Draw the two recursive branches: | |
if random.randint(0, 9) != 0: | |
turtle.left(LEFT_ANGLE) | |
drawBranch(turtle.xcor(), turtle.ycor(), turtle.heading(), branchLength - LEFT_DECREASE) | |
turtle.right(LEFT_ANGLE) | |
if random.randint(0, 9) != 0: | |
turtle.right(RIGHT_ANGLE) | |
drawBranch(turtle.xcor(), turtle.ycor(), turtle.heading(), branchLength - RIGHT_DECREASE) | |
turtle.left(RIGHT_ANGLE) | |
# Return back to the starting point: | |
turtle.penup() | |
turtle.goto(x, y) | |
turtle.setheading(direction) | |
turtle.pendown() | |
def drawTree(x, y, direction, seed): | |
global LEFT_ANGLE, RIGHT_ANGLE, LEFT_DECREASE, RIGHT_DECREASE | |
# Go to the starting point: | |
turtle.penup() | |
turtle.goto(x, y) | |
turtle.setheading(direction) | |
turtle.pendown() | |
# Try changing these values and looking at the results: | |
random.seed(seed) | |
LEFT_ANGLE = random.randint(10, 30) | |
RIGHT_ANGLE = random.randint(10, 30) | |
LEFT_DECREASE = random.randint( 6, 15) | |
RIGHT_DECREASE = random.randint( 6, 15) | |
START_SIZE = random.randint(80, 120) | |
# Draw the tree: | |
drawBranch(x, y, direction, START_SIZE) | |
turtle.update() # Finish drawing the screen. | |
try: | |
main() | |
except turtle.Terminator: | |
pass # Do nothing when the turtle window is closed. |
This file contains 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
"""Fractal Tree Drawer, by Al Sweigart [email protected] | |
Draws fractal trees with turtle graphics.""" | |
__version__ = 0 | |
import random | |
import time | |
import turtle | |
turtle.tracer(1000, 0) # Make the turtle draw faster. | |
turtle.setworldcoordinates(0, 0, 700, 700) | |
turtle.hideturtle() | |
def drawBranch(startPosition, direction, branchLength): | |
if branchLength < 5: | |
# Base case; the branches are two small to keep drawing more: | |
return | |
# Go to the starting point & direction: | |
turtle.penup() | |
turtle.goto(startPosition) | |
turtle.setheading(direction) | |
# Draw the branch (thickness is 1/7 the length): | |
turtle.pendown() | |
turtle.pensize(max(branchLength / 7.0, 1)) | |
turtle.forward(branchLength) | |
# Record the position of the branch's end: | |
endPosition = turtle.position() | |
leftDirection = direction + LEFT_ANGLE | |
leftBranchLength = branchLength - LEFT_DECREASE | |
rightDirection = direction - RIGHT_ANGLE | |
rightBranchLength = branchLength - RIGHT_DECREASE | |
# Recursive case; draw two more branches: | |
drawBranch(endPosition, leftDirection, leftBranchLength) | |
drawBranch(endPosition, rightDirection, rightBranchLength) | |
try: | |
seed = 0 | |
while True: # Main program loop. | |
# Get psuedorandom numbers for the branch properties: | |
random.seed(seed) | |
LEFT_ANGLE = random.randint(10, 30) | |
LEFT_DECREASE = random.randint( 6, 15) | |
RIGHT_ANGLE = random.randint(10, 30) | |
RIGHT_DECREASE = random.randint( 6, 15) | |
START_LENGTH = random.randint(80, 120) | |
# Write out the seed number: | |
turtle.clear() | |
turtle.penup() | |
turtle.goto(10, 10) | |
turtle.write('Seed: %s' % (seed)) | |
# Draw the tree: | |
drawBranch((350, 10), 90, START_LENGTH) | |
turtle.update() # Finish drawing the screen. | |
time.sleep(2) | |
seed = seed + 1 # Use the next number for the next seed. | |
except turtle.Terminator: | |
pass # Do nothing when the turtle window is closed. |
This file contains 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
"""Hex Grid, by Al Sweigart [email protected] | |
Displays a simple tessellation of a hexagon grid. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, beginner, artistic""" | |
__version__ = 0 | |
# Set up the constants: | |
# (!) Try changing these values to other numbers: | |
X_REPEAT = 19 # How many times to tessellate horizontally. | |
Y_REPEAT = 12 # How many times to tessellate vertically. | |
for y in range(Y_REPEAT): | |
# Display the top half of the hexagon: | |
for x in range(X_REPEAT): | |
print(r'/ \_', end='') | |
print() | |
# Display the bottom half of the hexagon: | |
for x in range(X_REPEAT): | |
print(r'\_/ ', end='') | |
print() |
This file contains 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
"""Hilbert Curve, by Al Sweigart [email protected] | |
Draws the Hilbert Curve fractal with turtle graphics. | |
More info at: https://en.wikipedia.org/wiki/hilbertCurve | |
Good videos on space-filling curves: https://youtu.be/RU0wScIj36o | |
and https://youtu.be/3s7h2MHQtxc""" | |
__version__ = 0 | |
import turtle | |
# Set up the constants: | |
SIZE = 10 # (!) Try changing the line length by a litte. | |
ANGLE = 90 # (!) Try changing the turning angle by a litte. | |
LEVEL = 5 # (!) Try changing the recursive level by a litte. | |
MAGENTA = '#B20059' | |
PINK = '#FFE6F2' | |
def main(): | |
turtle.bgcolor(MAGENTA) | |
turtle.pencolor(PINK) | |
turtle.fillcolor(PINK) | |
turtle.tracer(1, 0) # Make the turtle draw faster. | |
turtle.penup() | |
turtle.goto(-320, 0) | |
turtle.pendown() | |
#turtle.setheading(20) # (!) Try uncommenting this line. | |
filledInHilbert() | |
turtle.update() # Finish drawing the screen. | |
turtle.exitonclick() # When user clicks on the window, close it. | |
def hilbertCurve(level, angle): | |
if level == 0: | |
return | |
turtle.right(angle) | |
hilbertCurve(level - 1, -angle) | |
turtle.forward(SIZE) | |
turtle.left(angle) | |
hilbertCurve(level - 1, angle) | |
turtle.forward(SIZE) | |
hilbertCurve(level - 1, angle) | |
turtle.left(angle) | |
turtle.forward(SIZE) | |
hilbertCurve(level - 1, -angle) | |
turtle.right(angle) | |
def filledInHilbert(): | |
turtle.begin_fill() | |
hilbertCurve(LEVEL, ANGLE) # draw first quadrant | |
turtle.forward(SIZE) | |
hilbertCurve(LEVEL, ANGLE) # draw second quadrant | |
turtle.left(ANGLE) | |
turtle.forward(SIZE) | |
turtle.left(ANGLE) | |
hilbertCurve(LEVEL, ANGLE) # draw third quadrant | |
turtle.forward(SIZE) | |
hilbertCurve(LEVEL, ANGLE) # draw fourth quadrant | |
turtle.left(ANGLE) | |
turtle.forward(SIZE) | |
turtle.left(ANGLE) | |
turtle.end_fill() | |
try: | |
main() | |
except turtle.Terminator: | |
pass # Do nothing when the turtle window is closed. |
This file contains 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
"""Koch Snowflake, by Al Sweigart [email protected] | |
Draws a Koch snowflake fractal with turtle graphics.""" | |
__version__ = 0 | |
import turtle | |
turtle.tracer(1, 0) # Make the turtle draw faster. | |
LEVELS = 5 # More than 5 levels becomes too small to see. | |
def main(): | |
# Move turtle into the starting position: | |
LENGTH = 300.0 | |
turtle.penup() | |
turtle.backward(LENGTH / 2.0) | |
turtle.right(90) | |
turtle.backward(1.75 * LENGTH / 2.0) | |
turtle.left(90) | |
turtle.pendown() | |
# Draw the snowflake: | |
for lev in range(LEVELS): | |
drawSnowflake(LENGTH, lev) | |
turtle.update() # Finish drawing the screen. | |
turtle.exitonclick() # When user clicks on the window, close it. | |
def snowflakeSide(sideLength, levels): | |
# Draw a single side of the snowflake (this is called a Koch curve): | |
turtle.pencolor('black') | |
if levels == 0: | |
turtle.forward(sideLength) | |
return | |
sideLength = sideLength / 3.0 | |
snowflakeSide(sideLength, levels-1) | |
# "Erase" the middle segment by drawing a white line over it. | |
turtle.pencolor('white') | |
turtle.pensize(2) | |
turtle.forward(sideLength) | |
turtle.forward(-sideLength) | |
turtle.pencolor('black') | |
turtle.pensize(1) | |
turtle.left(60) | |
snowflakeSide(sideLength, levels-1) | |
turtle.right(120) | |
snowflakeSide(sideLength, levels-1) | |
turtle.left(60) | |
snowflakeSide(sideLength, levels-1) | |
def drawSnowflake(sideLength, levels): | |
# Draw 6 Koch curves to draw a Koch snowflake. | |
for i in range(6): | |
snowflakeSide(sideLength, levels) | |
turtle.right(60) | |
try: | |
main() | |
except turtle.Terminator: | |
pass # Do nothing when the turtle window is closed. |
This file contains 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
"""Maze Maker, by Al Sweigart [email protected] | |
Make mazes with the recursive backtracker algorithm. | |
An animated demo: https://scratch.mit.edu/projects/17358777/ | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, maze""" | |
__version__ = 0 | |
import random | |
# Set up the constants: | |
WALL = '#' | |
EMPTY = ' ' | |
START = 'S' | |
EXIT = 'E' | |
BLOCK = chr(9617) # Character 9617 is '░' | |
NORTH = 'north' | |
SOUTH = 'south' | |
EAST = 'east' | |
WEST = 'west' | |
def displayMaze(maze, travelerX=None, travelerY=None): | |
result = "" # speeding up drawing in Thonny by printing only one line | |
for y in range(HEIGHT): | |
for x in range(WIDTH): | |
if maze[(x, y)] == WALL: | |
result += BLOCK | |
#print(BLOCK, end='') | |
elif travelerX == x and travelerY == y: | |
result += "@" | |
#print('@', end='') | |
else: | |
result += maze[(x, y)] | |
#print(maze[(x, y)], end='') | |
#print() # Print a newline after printing the row. | |
result += "\n" | |
print(result) | |
def saveMaze(maze, filename): | |
mazeFile = open(filename, 'w') | |
for y in range(HEIGHT): | |
for x in range(WIDTH): | |
mazeFile.write(maze[(x, y)]) | |
mazeFile.write('\n') | |
mazeFile.close() | |
print('''Maze Maker (Recursive Backtracker algorithm) | |
By Al Sweigart [email protected] | |
This program creates maze files. You can play these mazes with | |
mazerunner.py or maze3d.py''') | |
WIDTH = 49 | |
while not WIDTH: | |
response = input('Enter width (must be odd and greater than 2): ') | |
if response.isdecimal(): | |
WIDTH = int(response) | |
if WIDTH % 2 == 1 and WIDTH > 2: | |
break | |
WIDTH = None | |
HEIGHT = 21 | |
while not HEIGHT: | |
response = input('Enter height (must be odd and greater than 2): ') | |
if response.isdecimal(): | |
HEIGHT = int(response) | |
if HEIGHT % 2 == 1 and HEIGHT > 2: | |
break | |
HEIGHT = None | |
SEED = random.randrange(10000) | |
while not SEED: | |
response = input('Enter seed (must be a positive integer): ') | |
if response.isdecimal(): | |
SEED = int(response) | |
break | |
SEED = None | |
random.seed(SEED) | |
#response = input('Watch maze generation step by step? (y/n): ') | |
#watchGeneration = response.upper().startswith('Y') | |
watchGeneration = True | |
# Create the filled-in maze to start: | |
maze = {} | |
for x in range(WIDTH): | |
for y in range(HEIGHT): | |
maze[(x, y)] = WALL | |
# Create the maze: | |
print('Generating maze...') | |
pathFromStart = [(1, 1)] | |
hasVisited = [(1, 1)] | |
while len(pathFromStart) > 0: | |
x, y = pathFromStart[-1] | |
maze[(x, y)] = EMPTY | |
# Display the maze so far: | |
if watchGeneration: | |
displayMaze(maze, x, y) | |
# (!) Uncomment this next line to see how pathFromStart grows | |
# and shrinks while the algorithm runs. | |
#print('pathFromStart =', pathFromStart) | |
print('Press Enter to continue...') | |
input() | |
print('\n' * 60) # Clear the screen by printing newlines. | |
unvisitedNeighbors = [] | |
# Check the north neighbor: | |
if y > 1 and (x, y - 2) not in hasVisited: | |
unvisitedNeighbors.append(NORTH) | |
# Check the south neighbor: | |
if y < HEIGHT - 2 and (x, y + 2) not in hasVisited: | |
unvisitedNeighbors.append(SOUTH) | |
# Check the west neighbor: | |
if x > 1 and (x - 2, y) not in hasVisited: | |
unvisitedNeighbors.append(WEST) | |
# Check the east neighbor: | |
if x < WIDTH - 2 and (x + 2, y) not in hasVisited: | |
unvisitedNeighbors.append(EAST) | |
if len(unvisitedNeighbors) > 0: | |
nextIntersection = random.choice(unvisitedNeighbors) | |
if nextIntersection == NORTH: | |
pathFromStart.append((x, y - 2)) | |
hasVisited.append((x, y - 2)) | |
maze[(x, y - 1)] = EMPTY | |
elif nextIntersection == SOUTH: | |
pathFromStart.append((x, y + 2)) | |
hasVisited.append((x, y + 2)) | |
maze[(x, y + 1)] = EMPTY | |
elif nextIntersection == WEST: | |
pathFromStart.append((x - 2, y)) | |
hasVisited.append((x - 2, y)) | |
maze[(x - 1, y)] = EMPTY | |
elif nextIntersection == EAST: | |
pathFromStart.append((x + 2, y)) | |
hasVisited.append((x + 2, y)) | |
maze[(x + 1, y)] = EMPTY | |
else: | |
pathFromStart.pop() | |
# Add the start and end positions: | |
maze[(1, 1)] = START | |
maze[(WIDTH - 2, HEIGHT - 2)] = EXIT | |
# Display the maze and save it to a text file. | |
displayMaze(maze) | |
filename = 'maze{}x{}s{}.txt'.format(WIDTH, HEIGHT, SEED) | |
saveMaze(maze, filename) | |
print('Saved to {}.'.format(filename)) |
This file contains 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
"""Maze Runner 2D, by Al Sweigart [email protected] | |
Move around a maze and try to escape. Maze files are generated by | |
mazemakerrec.py. | |
This and other games are available at https://nostarch.com/XX | |
Tags: large, maze""" | |
__version__ = 0 | |
import sys, os | |
# Maze file constants: | |
WALL = '#' | |
EMPTY = ' ' | |
START = 'S' | |
EXIT = 'E' | |
PLAYER = '@' | |
BLOCK = chr(9617) # Character 9617 is '░' | |
def displayMaze(maze): | |
# Display the maze: | |
for y in range(HEIGHT): | |
for x in range(WIDTH): | |
if (x, y) == (playerx, playery): | |
print(PLAYER, end='') | |
elif (x, y) == (exitx, exity): | |
print('X', end='') | |
elif maze[(x, y)] == WALL: | |
print(BLOCK, end='') | |
else: | |
print(maze[(x, y)], end='') | |
print() # Print a newline after printing the row. | |
print('''Maze Runner 2D, by Al Sweigart [email protected] | |
(Maze files are generated by mazemakerrec.py)''') | |
# Get the maze file's filename from the user: | |
while True: | |
print('Enter the filename of the maze (or LIST or QUIT):') | |
filename = input('> ') | |
# List all the maze files in the current folder: | |
if filename.upper() == 'LIST': | |
print('Maze files found in', os.getcwd()) | |
for fileInCurrentFolder in os.listdir(): | |
if (fileInCurrentFolder.startswith('maze') and | |
fileInCurrentFolder.endswith('.txt')): | |
print(' ', fileInCurrentFolder) | |
continue | |
if filename.upper() == 'QUIT': | |
sys.exit() | |
if os.path.exists(filename): | |
break | |
print('There is no file named', filename) | |
# Load the maze from a file: | |
mazeFile = open(filename) | |
maze = {} | |
lines = mazeFile.readlines() | |
playerx = None | |
playery = None | |
exitx = None | |
exity = None | |
y = 0 | |
for line in lines: | |
WIDTH = len(line.rstrip()) | |
for x, character in enumerate(line.rstrip()): | |
assert character in (WALL, EMPTY, START, EXIT), 'Invalid character at column {}, line {}'.format(x + 1, y + 1) | |
if character in (WALL, EMPTY): | |
maze[(x, y)] = character | |
elif character == START: | |
playerx, playery = x, y | |
maze[(x, y)] = EMPTY | |
elif character == EXIT: | |
exitx, exity = x, y | |
maze[(x, y)] = EMPTY | |
y += 1 | |
HEIGHT = y | |
assert playerx != None and playery != None, 'No start in maze file.' | |
assert exitx != None and exity != None, 'No exit in maze file.' | |
forward_moves = [] | |
def get_move(): | |
global forward_moves | |
if forward_moves: | |
return (False, forward_moves.pop(0)) | |
else: | |
print(' W') | |
print('Enter direction, or QUIT: ASD') | |
moves = input('> ').upper() | |
if len(moves) > 1: | |
if moves == 'QUIT': | |
return (False, 'QUIT') | |
forward_moves = list(moves) | |
return (True, []) | |
return (False, moves) | |
while True: # Main game loop. | |
displayMaze(maze) | |
while True: # Get user move. | |
skip, move = get_move() | |
if skip: | |
continue | |
if move == 'QUIT': | |
print('Thanks for playing!') | |
sys.exit() | |
if move not in ['W', 'A', 'S', 'D']: | |
print('Invalid direction. Enter one of W, A, S, or D.') | |
continue | |
# Check if the player can move in that direction: | |
if move == 'W' and maze[(playerx, playery - 1)] == EMPTY: | |
break | |
elif move == 'S' and maze[(playerx, playery + 1)] == EMPTY: | |
break | |
elif move == 'A' and maze[(playerx - 1, playery)] == EMPTY: | |
break | |
elif move == 'D' and maze[(playerx + 1, playery)] == EMPTY: | |
break | |
print('You cannot move in that direction.') | |
# Keep moving in this direction until you encounter a branch point. | |
if move == 'W': | |
while True: | |
playery -= 1 | |
if (playerx, playery) == (exitx, exity): | |
break | |
if maze[(playerx, playery - 1)] == WALL: | |
break # Break if we've hit a wall. | |
if (maze[(playerx - 1, playery)] == EMPTY | |
or maze[(playerx + 1, playery)] == EMPTY): | |
break # Break if we've reached a branch point. | |
elif move == 'S': | |
while True: | |
playery += 1 | |
if (playerx, playery) == (exitx, exity): | |
break | |
if maze[(playerx, playery + 1)] == WALL: | |
break # Break if we've hit a wall. | |
if (maze[(playerx - 1, playery)] == EMPTY | |
or maze[(playerx + 1, playery)] == EMPTY): | |
break # Break if we've reached a branch point. | |
elif move == 'A': | |
while True: | |
playerx -= 1 | |
if (playerx, playery) == (exitx, exity): | |
break | |
if maze[(playerx - 1, playery)] == WALL: | |
break # Break if we've hit a wall. | |
if (maze[(playerx, playery - 1)] == EMPTY | |
or maze[(playerx, playery + 1)] == EMPTY): | |
break # Break if we've reached a branch point. | |
elif move == 'D': | |
while True: | |
playerx += 1 | |
if (playerx, playery) == (exitx, exity): | |
break | |
if maze[(playerx + 1, playery)] == WALL: | |
break # Break if we've hit a wall. | |
if (maze[(playerx, playery - 1)] == EMPTY | |
or maze[(playerx, playery + 1)] == EMPTY): | |
break # Break if we've reached a branch point. | |
if (playerx, playery) == (exitx, exity): | |
displayMaze(maze) | |
print('You have reached the exit! Good job!') | |
print('Thanks for playing!') | |
sys.exit() |
This file contains 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
"""Maze 3D, by Al Sweigart [email protected] | |
Move around a maze and try to escape... in 3D! | |
This and other games are available at https://nostarch.com/XX | |
Tags: extra-large, maze, game, artistic""" | |
__version__ = 0 | |
import copy, sys, os | |
# Set up the constants: | |
WALL = '#' | |
EMPTY = ' ' | |
START = 'S' | |
EXIT = 'E' | |
BLOCK = chr(9617) # Character 9617 is '░' | |
NORTH = 'NORTH' | |
SOUTH = 'SOUTH' | |
EAST = 'EAST' | |
WEST = 'WEST' | |
def wallStrToWallDict(wallStr): | |
"""Takes a string representation of a wall drawing (like those in | |
ALL_OPEN or CLOSED) and returns a representation in a dictionary | |
with (x, y) tuples as keys and single-character strings of the | |
character to draw at that x, y location.""" | |
wallDict = {} | |
height = 0 | |
width = 0 | |
for y, line in enumerate(wallStr.splitlines()): | |
if y > height: | |
height = y | |
for x, character in enumerate(line): | |
if x > width: | |
width = x | |
wallDict[(x, y)] = character | |
wallDict['height'] = height + 1 | |
wallDict['width'] = width + 1 | |
return wallDict | |
EXIT_DICT = {(0, 0): 'E', (1, 0): 'X', (2, 0): 'I', | |
(3, 0): 'T', 'height': 1, 'width': 4} | |
# The way we create the strings to display is by converting the pictures | |
# in these multiline strings to dictionaries using wallStrToWallDict(). | |
# Then we compose the wall for the player's location and direction by | |
# "pasting" the wall dictionaries in CLOSED on top of the wall dictionary | |
# in ALL_OPEN. | |
ALL_OPEN = wallStrToWallDict(r''' | |
................. | |
____.........____ | |
...|\......./|... | |
...||.......||... | |
...||__...__||... | |
...||.|\./|.||... | |
...||.|.X.|.||... | |
...||.|/.\|.||... | |
...||_/...\_||... | |
...||.......||... | |
___|/.......\|___ | |
................. | |
.................'''.strip()) | |
# The strip() call is used to remove the newline | |
# at the start of this multiline string. | |
CLOSED = {} | |
CLOSED['A'] = wallStrToWallDict(r''' | |
_____ | |
..... | |
..... | |
..... | |
_____'''.strip()) # Paste to 6, 4. | |
CLOSED['B'] = wallStrToWallDict(r''' | |
.\. | |
..\ | |
... | |
... | |
... | |
../ | |
./.'''.strip()) # Paste to 4, 3. | |
CLOSED['C'] = wallStrToWallDict(r''' | |
___________ | |
........... | |
........... | |
........... | |
........... | |
........... | |
........... | |
........... | |
........... | |
___________'''.strip()) # Paste to 3, 1. | |
CLOSED['D'] = wallStrToWallDict(r''' | |
./. | |
/.. | |
... | |
... | |
... | |
\.. | |
.\.'''.strip()) # Paste to 10, 3. | |
CLOSED['E'] = wallStrToWallDict(r''' | |
..\.. | |
...\_ | |
....| | |
....| | |
....| | |
....| | |
....| | |
....| | |
....| | |
....| | |
....| | |
.../. | |
../..'''.strip()) # Paste to 0, 0. | |
CLOSED['F'] = wallStrToWallDict(r''' | |
../.. | |
_/... | |
|.... | |
|.... | |
|.... | |
|.... | |
|.... | |
|.... | |
|.... | |
|.... | |
|.... | |
.\... | |
..\..'''.strip()) # Paste to 12, 0. | |
def displayWallDict(wallDict): | |
"""Display a wall dictionary, as returned by wallStrToWallDict(), on | |
the screen.""" | |
print(BLOCK * (wallDict['width'] + 2)) | |
for y in range(wallDict['height']): | |
print(BLOCK, end='') | |
for x in range(wallDict['width']): | |
wall = wallDict[(x, y)] | |
if wall == '.': | |
wall = ' ' | |
print(wall, end='') | |
print(BLOCK) # Print block with a newline. | |
print(BLOCK * (wallDict['width'] + 2)) | |
def pasteWallDict(srcWallDict, dstWallDict, left, top): | |
"""Copy the wall representation dictionary in srcWallDict on top of | |
the one in dstWallDict, offset to the position given by left, top.""" | |
dstWallDict = copy.copy(dstWallDict) | |
for x in range(srcWallDict['width']): | |
for y in range(srcWallDict['height']): | |
dstWallDict[(x + left, y + top)] = srcWallDict[(x, y)] | |
return dstWallDict | |
def makeWallDict(maze, playerx, playery, playerDirection, exitx, exity): | |
"""From the player's position and direction in the maze (which has | |
an exit at exitx, exity), create the wall representation dictionary | |
by pasting wall dictionaries on top of ALL_OPEN, then return it.""" | |
# The A-F "sections" (which are relative to the player's direction) | |
# determine which walls in the maze we check to see if we need to | |
# paste them over the wall representation dictionary we're creating. | |
if playerDirection == NORTH: | |
# Map of the sections, relative A | |
# to the player @: BCD (Player facing north) | |
# E@F | |
offsets = (('A', 0, -2), ('B', -1, -1), ('C', 0, -1), | |
('D', 1, -1), ('E', -1, 0), ('F', 1, 0)) | |
if playerDirection == SOUTH: | |
# Map of the sections, relative F@E | |
# to the player @: DCB (Player facing south) | |
# A | |
offsets = (('A', 0, 2), ('B', 1, 1), ('C', 0, 1), | |
('D', -1, 1), ('E', 1, 0), ('F', -1, 0)) | |
if playerDirection == EAST: | |
# Map of the sections, relative EB | |
# to the player @: @CA (Player facing east) | |
# FD | |
offsets = (('A', 2, 0), ('B', 1, -1), ('C', 1, 0), | |
('D', 1, 1), ('E', 0, -1), ('F', 0, 1)) | |
if playerDirection == WEST: | |
# Map of the sections, relative DF | |
# to the player @: AC@ (Player facing west) | |
# BE | |
offsets = (('A', -2, 0), ('B', -1, 1), ('C', -1, 0), | |
('D', -1, -1), ('E', 0, 1), ('F', 0, -1)) | |
section = {} | |
for sec, xOff, yOff in offsets: | |
section[sec] = maze.get((playerx + xOff, playery + yOff), WALL) | |
if (playerx + xOff, playery + yOff) == (exitx, exity): | |
section[sec] = EXIT | |
wallDict = copy.copy(ALL_OPEN) | |
PASTE_CLOSED_TO = {'A': (6, 4), 'B': (4, 3), 'C': (3, 1), | |
'D': (10, 3), 'E': (0, 0), 'F': (12, 0)} | |
for sec in 'ABDCEF': | |
if section[sec] == WALL: | |
wallDict = pasteWallDict(CLOSED[sec], wallDict, | |
PASTE_CLOSED_TO[sec][0], PASTE_CLOSED_TO[sec][1]) | |
# Draw the EXIT sign if needed: | |
if section['C'] == EXIT: | |
wallDict = pasteWallDict(EXIT_DICT, wallDict, 7, 9) | |
if section['E'] == EXIT: | |
wallDict = pasteWallDict(EXIT_DICT, wallDict, 0, 11) | |
if section['F'] == EXIT: | |
wallDict = pasteWallDict(EXIT_DICT, wallDict, 13, 11) | |
return wallDict | |
print('Maze Runner 3D, by Al Sweigart [email protected]') | |
print('(Maze files are generated by mazemakerrec.py)') | |
# Get the maze file's filename from the user: | |
while True: | |
print('Enter the filename of the maze (or LIST or QUIT):') | |
filename = input('> ') | |
# List all the maze files in the current folder: | |
if filename.upper() == 'LIST': | |
print('Maze files found in', os.getcwd()) | |
for fileInCurrentFolder in os.listdir(): | |
if (fileInCurrentFolder.startswith('maze') | |
and fileInCurrentFolder.endswith('.txt')): | |
print(' ', fileInCurrentFolder) | |
continue | |
if filename.upper() == 'QUIT': | |
sys.exit() | |
if os.path.exists(filename): | |
break | |
print('There is no file named', filename) | |
# Load the maze from a file: | |
mazeFile = open(filename) | |
maze = {} | |
lines = mazeFile.readlines() | |
px = None | |
py = None | |
exitx = None | |
exity = None | |
y = 0 | |
for line in lines: | |
WIDTH = len(line.rstrip()) | |
for x, character in enumerate(line.rstrip()): | |
assert character in (WALL, EMPTY, START, EXIT), 'Invalid character at column {}, line {}'.format(x + 1, y + 1) | |
if character in (WALL, EMPTY): | |
maze[(x, y)] = character | |
elif character == START: | |
px, py = x, y | |
maze[(x, y)] = EMPTY | |
elif character == EXIT: | |
exitx, exity = x, y | |
maze[(x, y)] = EMPTY | |
y += 1 | |
HEIGHT = y | |
assert px != None and py != None, 'No start point in file.' | |
assert exitx != None and exity != None, 'No exit point in file.' | |
pDir = NORTH | |
while True: # Main game loop. | |
displayWallDict(makeWallDict(maze, px, py, pDir, exitx, exity)) | |
while True: # Get user move. | |
print('Location ({}, {}) Direction: {}'.format(px, py, pDir)) | |
print(' (W)') | |
print('Enter direction: (A) (D) or QUIT.') | |
move = input('> ').upper() | |
if move == 'QUIT': | |
print('Thanks for playing!') | |
sys.exit() | |
if (move not in ['F', 'L', 'R', 'W', 'A', 'D'] | |
and not move.startswith('T')): | |
print('Please enter one of F, L, or R (or W, A, D).') | |
continue | |
# Move the player according to their intended move: | |
if move == 'F' or move == 'W': | |
if pDir == NORTH and maze[(px, py - 1)] == EMPTY: | |
py -= 1 | |
break | |
if pDir == SOUTH and maze[(px, py + 1)] == EMPTY: | |
py += 1 | |
break | |
if pDir == EAST and maze[(px + 1, py)] == EMPTY: | |
px += 1 | |
break | |
if pDir == WEST and maze[(px - 1, py)] == EMPTY: | |
px -= 1 | |
break | |
elif move == 'L' or move == 'A': | |
pDir = {NORTH: WEST, WEST: SOUTH, | |
SOUTH: EAST, EAST: NORTH}[pDir] | |
break | |
elif move == 'R' or move == 'D': | |
pDir = {NORTH: EAST, EAST: SOUTH, | |
SOUTH: WEST, WEST: NORTH}[pDir] | |
break | |
elif move.startswith('T'): # Cheat code: 'T x,y' | |
px, py = move.split()[1].split(',') | |
px = int(px) | |
py = int(py) | |
break | |
else: | |
print('You cannot move in that direction.') | |
if (px, py) == (exitx, exity): | |
print('You have reached the exit! Good job!') | |
print('Thanks for playing!') | |
sys.exit() |
This file contains 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
# Взято з https://github.com/ripexz/python-tkinter-minesweeper | |
from tkinter import * | |
from tkinter import messagebox as tkMessageBox | |
from collections import deque | |
import random | |
import platform | |
import time | |
from datetime import time, date, datetime | |
import base64 | |
SIZE_X = 10 | |
SIZE_Y = 10 | |
STATE_DEFAULT = 0 | |
STATE_CLICKED = 1 | |
STATE_FLAGGED = 2 | |
BTN_CLICK = "<Button-1>" | |
BTN_FLAG = "<Button-2>" if platform.system() == 'Darwin' else "<Button-3>" | |
window = None | |
images = {} | |
images["tile_wrong.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5EKA2(K6zs0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!Ye_^wRCt_Y*hwN_0OGks1B8ZEL;!RR3}674zQx1}VZg<)Yk<fBRYNo&3u18+TpFSQSv7VI$a>IJ<J5p`FtTduX&}u>a7Ce^p@cn+EDZ@LG<)zxTxDe?kPRg9MI1qZqJgx;M-l)4RfUH1w2DYz00000NkvXXu0mjf' | |
images["tile_2.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5ElkG68s|b0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!KS@MERCt_YcuXQ-pu2_~rdwzTp9W0Tn0l~iz%PPN17RDmIuD;82&%@e0gq}d8t|(|*Fa{llAwVYKzj`UFP37=@pX8200000NkvXXu0mjf' | |
images["tile_3.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5EljmqxZ|#0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!I!Q!9RCt_YcuXQ-7zhn}m~J5>0vga%W9q@K0T-9BA4t$ZtRKkNK&+EU_B6@Cim8V*KVSmdYXAV?MSNA%eJ*+c0000<MNUMnLSTX' | |
images["tile_7.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5Ela<%7J6u0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!ElET{RCt_YcuXQ-ps$8oOsrTD^)z4t(llVIrm6<4s;Q>|m+Ar3fCtcC0|3`$a36jYD>(oF002ovPDHLkV1f' | |
images["tile_plain.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj4-qp8bnS}10000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!B1uF+RCt_Y*hwN_0OGks1B8ZEM1WBZqZ)|OKw9D>2><{l4*<XNp=LP%0000<MNUMnLSTX' | |
images["tile_mine.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj4-qvRNCyqd0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!IY~r8RCt_Y*hwN_0OGks1B8ZEM1X<Pz`(!&A>j<dHZULoqHI6|)Yd?blSuY7E<k4uq$NI*006ed$mpFe*RB8n002ovPDHLkV1f' | |
images["tile_4.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5Elg<a@D1G0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!DM>^@RCt_YcuXQ-pu2`!Ov`}?7nfoUn9{V-fQhK50TT=d4VZxT8UW52doM#7A9er$002ovPDHLkV1f' | |
images["tile_6.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5Eld!jCQ#(0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!Gf6~2RCt_YcuXQ-pu2`!Osr@Kp9W0Tn0l~iAi+swYM`$lXrqAyCt=lt&(p*J+G_v+yp%^L*oKYG00000NkvXXu0mjf' | |
images["tile_clicked.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj4-+UP0*RP~0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!6iGxuRCt_YcuXQ-7}YSUVfbnQ00=FqAHgP?hX4Qo07*qoM6N<$f&' | |
images["tile_8.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5ElYAktoXW0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!Gf6~2RCt_YcuXQ-pu2`!Osr@Kp9W0Tn0l~iz%N3X29o_ivIg4wf#hH%*$>13+G_v+Z_Y(u0Q+>$00000NkvXXu0mjf' | |
images["tile_flag.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj4-q#7fId<y0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!R!KxbRCt_Y*hwN_0OGks1B8ZEL;wOBZZWZvpaH0w1Pu_?G}8cAO_~N0JPlVzS}28vh5|JJNn$lHFfaf$07+ssR9033RRc+4HDCoO8c0igBmn?0jNwj~A7J?a0000<MNUMnLSTX' | |
images["tile_5.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5EleFSIuze0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!F-b&0RCt_YcuXQ-ps$8oOkr3NwKb6JB#Qh%vIYWvzyu_Fn%cpNRS%j1I;SOK008jsS_M8d$OZra002ovPDHLkV1f' | |
images["tile_1.png"] = b'iBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHUIzs2XskIMF-sj5Elm>K;y^t0000NbVXQnQ*UN;cVTj607pzjP)<i6c4cxPGcGi7g{0vC000Pda85@@OhhvP0000=fGHmU000DMK}|sb0I`n?{9y$E000JJOGiWi{{a60|De66lK=n!FG)l}RCt_YcuXQ-7$6POOt*lDSPc->#A<-6rkMt0Z9`Q9vZt|XqgVq$Kzj`Uw(fB)KLFlz00000NkvXXu0mjf' | |
def unpack_images(images): | |
""" | |
Картинки в коді запаковані в Base85. Потрібно перетворити в PhotoImage | |
""" | |
for name, im in images.items(): | |
data = base64.b85decode(im) | |
image = PhotoImage(format="png", data=data) | |
images[name] = image | |
class Minesweeper: | |
def __init__(self, tk): | |
# import images | |
self.images = { | |
"plain": images['tile_plain.png'], | |
"clicked": images['tile_clicked.png'], | |
"mine": images['tile_mine.png'], | |
"flag": images['tile_flag.png'], | |
"wrong": images['tile_wrong.png'], | |
"numbers": [] | |
} | |
for i in range(1, 9): | |
self.images["numbers"].append(images["tile_"+str(i)+".png"]) | |
# set up frame | |
self.tk = tk | |
self.frame = Frame(self.tk) | |
self.frame.pack() | |
# set up labels/UI | |
self.labels = { | |
"time": Label(self.frame, text = "00:00:00"), | |
"mines": Label(self.frame, text = "Mines: 0"), | |
"flags": Label(self.frame, text = "Flags: 0") | |
} | |
self.labels["time"].grid(row = 0, column = 0, columnspan = SIZE_Y) # top full width | |
self.labels["mines"].grid(row = SIZE_X+1, column = 0, columnspan = int(SIZE_Y/2)) # bottom left | |
self.labels["flags"].grid(row = SIZE_X+1, column = int(SIZE_Y/2)-1, columnspan = int(SIZE_Y/2)) # bottom right | |
self.restart() # start game | |
self.updateTimer() # init timer | |
def setup(self): | |
# create flag and clicked tile variables | |
self.flagCount = 0 | |
self.correctFlagCount = 0 | |
self.clickedCount = 0 | |
self.startTime = None | |
# create buttons | |
self.tiles = dict({}) | |
self.mines = 0 | |
for x in range(0, SIZE_X): | |
for y in range(0, SIZE_Y): | |
if y == 0: | |
self.tiles[x] = {} | |
id = str(x) + "_" + str(y) | |
isMine = False | |
# tile image changeable for debug reasons: | |
gfx = self.images["plain"] | |
# currently random amount of mines | |
if random.uniform(0.0, 1.0) < 0.1: | |
isMine = True | |
self.mines += 1 | |
tile = { | |
"id": id, | |
"isMine": isMine, | |
"state": STATE_DEFAULT, | |
"coords": { | |
"x": x, | |
"y": y | |
}, | |
"button": Button(self.frame, image = gfx), | |
"mines": 0 # calculated after grid is built | |
} | |
tile["button"].bind(BTN_CLICK, self.onClickWrapper(x, y)) | |
tile["button"].bind(BTN_FLAG, self.onRightClickWrapper(x, y)) | |
tile["button"].grid( row = x+1, column = y ) # offset by 1 row for timer | |
self.tiles[x][y] = tile | |
# loop again to find nearby mines and display number on tile | |
for x in range(0, SIZE_X): | |
for y in range(0, SIZE_Y): | |
mc = 0 | |
for n in self.getNeighbors(x, y): | |
mc += 1 if n["isMine"] else 0 | |
self.tiles[x][y]["mines"] = mc | |
def restart(self): | |
self.setup() | |
self.refreshLabels() | |
def refreshLabels(self): | |
self.labels["flags"].config(text = "Flags: "+str(self.flagCount)) | |
self.labels["mines"].config(text = "Mines: "+str(self.mines)) | |
def gameOver(self, won): | |
for x in range(0, SIZE_X): | |
for y in range(0, SIZE_Y): | |
if self.tiles[x][y]["isMine"] == False and self.tiles[x][y]["state"] == STATE_FLAGGED: | |
self.tiles[x][y]["button"].config(image = self.images["wrong"]) | |
if self.tiles[x][y]["isMine"] == True and self.tiles[x][y]["state"] != STATE_FLAGGED: | |
self.tiles[x][y]["button"].config(image = self.images["mine"]) | |
self.tk.update() | |
msg = "You Win! Play again?" if won else "You Lose! Play again?" | |
res = tkMessageBox.askyesno("Game Over", msg) | |
if res: | |
self.restart() | |
else: | |
self.tk.quit() | |
def updateTimer(self): | |
ts = "00:00:00" | |
if self.startTime != None: | |
delta = datetime.now() - self.startTime | |
ts = str(delta).split('.')[0] # drop ms | |
if delta.total_seconds() < 36000: | |
ts = "0" + ts # zero-pad | |
self.labels["time"].config(text = ts) | |
self.frame.after(100, self.updateTimer) | |
def getNeighbors(self, x, y): | |
neighbors = [] | |
coords = [ | |
{"x": x-1, "y": y-1}, #top right | |
{"x": x-1, "y": y}, #top middle | |
{"x": x-1, "y": y+1}, #top left | |
{"x": x, "y": y-1}, #left | |
{"x": x, "y": y+1}, #right | |
{"x": x+1, "y": y-1}, #bottom right | |
{"x": x+1, "y": y}, #bottom middle | |
{"x": x+1, "y": y+1}, #bottom left | |
] | |
for n in coords: | |
try: | |
neighbors.append(self.tiles[n["x"]][n["y"]]) | |
except KeyError: | |
pass | |
return neighbors | |
def onClickWrapper(self, x, y): | |
return lambda Button: self.onClick(self.tiles[x][y]) | |
def onRightClickWrapper(self, x, y): | |
return lambda Button: self.onRightClick(self.tiles[x][y]) | |
def onClick(self, tile): | |
if self.startTime == None: | |
self.startTime = datetime.now() | |
if tile["isMine"] == True: | |
# end game | |
self.gameOver(False) | |
return | |
# change image | |
if tile["mines"] == 0: | |
tile["button"].config(image = self.images["clicked"]) | |
self.clearSurroundingTiles(tile["id"]) | |
else: | |
tile["button"].config(image = self.images["numbers"][tile["mines"]-1]) | |
# if not already set as clicked, change state and count | |
if tile["state"] != STATE_CLICKED: | |
tile["state"] = STATE_CLICKED | |
self.clickedCount += 1 | |
if self.clickedCount == (SIZE_X * SIZE_Y) - self.mines: | |
self.gameOver(True) | |
def onRightClick(self, tile): | |
if self.startTime == None: | |
self.startTime = datetime.now() | |
# if not clicked | |
if tile["state"] == STATE_DEFAULT: | |
tile["button"].config(image = self.images["flag"]) | |
tile["state"] = STATE_FLAGGED | |
tile["button"].unbind(BTN_CLICK) | |
# if a mine | |
if tile["isMine"] == True: | |
self.correctFlagCount += 1 | |
self.flagCount += 1 | |
self.refreshLabels() | |
# if flagged, unflag | |
elif tile["state"] == 2: | |
tile["button"].config(image = self.images["plain"]) | |
tile["state"] = 0 | |
tile["button"].bind(BTN_CLICK, self.onClickWrapper(tile["coords"]["x"], tile["coords"]["y"])) | |
# if a mine | |
if tile["isMine"] == True: | |
self.correctFlagCount -= 1 | |
self.flagCount -= 1 | |
self.refreshLabels() | |
def clearSurroundingTiles(self, id): | |
queue = deque([id]) | |
while len(queue) != 0: | |
key = queue.popleft() | |
parts = key.split("_") | |
x = int(parts[0]) | |
y = int(parts[1]) | |
for tile in self.getNeighbors(x, y): | |
self.clearTile(tile, queue) | |
def clearTile(self, tile, queue): | |
if tile["state"] != STATE_DEFAULT: | |
return | |
if tile["mines"] == 0: | |
tile["button"].config(image = self.images["clicked"]) | |
queue.append(tile["id"]) | |
else: | |
tile["button"].config(image = self.images["numbers"][tile["mines"]-1]) | |
tile["state"] = STATE_CLICKED | |
self.clickedCount += 1 | |
### END OF CLASSES ### | |
def main(): | |
# create Tk instance | |
window = Tk() | |
unpack_images(images) | |
# set program title | |
window.title("Minesweeper") | |
# create game instance | |
minesweeper = Minesweeper(window) | |
# run event loop | |
window.mainloop() | |
if __name__ == "__main__": | |
main() |
This file contains 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
"""Mona Lisa, by Al Sweigart [email protected] | |
Draws an Andy Warhol-like drawing of the Mona Lisa with turtle | |
graphics.""" | |
__version__ = 0 | |
import turtle | |
turtle.tracer(400, 0) # Make the turtle draw faster. | |
"""The image data for the Mona Lisa. I created this through a complicated | |
process: I found a picture of the Mona Lisa online, converted it to | |
grayscale in Photoshop, then greatly increased the contrast, then shrunk | |
it down, and converted that image into a pure black/white image. I wrote | |
a script using the Pillow module to turn the black/white pixel | |
information in to a stream of 1s and 0s, and then turned that binary | |
number into this hexadecimal number.""" | |
monaLisaData = '0x54a9554ebaaab5555b776eeb56addebdb5db5b33fd9b6d5d6db55affcaeed576d559dd71576ab7a9a76ee32ceb59b556edd591df6b5aead5b265add256954aa52ad5aa55aa96ab55fd576d569d2b556affea992a955b4aa94effd4dd555496aa57f7feb45554a51534b9dfecb2aa36caa4a627ff14a49c254922d12ffd69345b54552c037f88a951423249a89ffe6905494892bc44bfda6689e74925a22bfd7125432a927800bff9d24bdeac83b5edfef6935fb7757fbbfff6d10adddd4ba9b5ffff4d5eeef37a913ff55255fabaff86aaffff92aafffd59103feafaadfb6fffc99fffe8ab5bff5ffc947ffffbdffd6f7f571ffffeeb6f7bfefe3d57eeffffffff77d9afbf7f5b7bbd7ffe5b7fff7efbff7fbff29fffafbffeffdebf97ffffdfedff6ffffdffffded7feffdd6fffffff7fd5fdb76ffedefffffffffffb7ff77fbb7dbbfef5b7feb57fdd6ddbf5efbdeb5bfffd6feeffdffe9afffdedefbb7fff8227fefafbfdfbefe5116bfcbbb7eeffde048fffe4dddfbbffca027ffbb6ff75f7fa090bf7fdd7bbabdfc0096fbee33ffdf7e2484ffbfbd1ddebff000170dffbef7fcfca910affffe9fb5ffe00897bffffdbdc7ff90017fffffefabffee805ffffeafefefefb757beefffb76ebf7fbfffffbffbf76ffbeedbfffffdffdbdffff7ffffffffffffffbbeff6bfefb76ffffdffffff7fbffb3fbfffffffbbfefd59efffdbefeffffbeafffffffffffffff7f7fefffffffffeedfbeffedfbffffffffeffffffbffeffffff7efdf7ffffffffff7fffefffffffffdfffeffffffbefffffbfbffdffffffff7bffff7ffffffffbfffffffbdfffbbdfffffffbdffebbffffffffffffff7efffffffffffff7feff5ffffff7f7ffbf76f05ffdffdfffff7bf892bffffffdfffffbe4a5fffffffefffffd50affffffffffffdf6a43fffffffffffffbb51f7fdfbfffffffd4baad57ffdfbfffd6b4f7ffffffffffff3ae7affffffffbff5be73f77effffeff7e8bbdffffffddffff5bfcefbf7ffffff7fd8def7fffefffffffeffffbfffffffffffb7fffffffffffffffefb77fffffffffffffffffffffffbffffffbfffffffffffffffffffffffffffffff7fffffffffffffffffffffff7ffffffffffffffffffffffff7ff7ffdfffffffeffffffffffffffffffffffff7fffffffffffffffffffffffffff' | |
def main(): | |
turtle.bgcolor('#FFF7D0') | |
turtle.fillcolor('#FF0C6B') | |
drawFromData(monaLisaData, 68, 100, -272, 400, 4) | |
turtle.fillcolor('#CE18FF') | |
drawFromData(monaLisaData, 68, 100, 0, 400, 4) | |
turtle.fillcolor('#7C0BE8') | |
drawFromData(monaLisaData, 68, 100, -272, 0, 4) | |
turtle.fillcolor('#460CFF') | |
drawFromData(monaLisaData, 68, 100, 0, 0, 4) | |
turtle.update() # Finish drawing the screen. | |
turtle.exitonclick() # When user clicks on the window, close it. | |
def drawFromData(image_data, image_width, image_height, left, top, pixel_size): | |
turtle.penup() | |
inBinary = bin(int(image_data, 16)) | |
# Remove '0b' from the start of the hex number: | |
inBinary = inBinary[2:] | |
# Add leading zeros to the binary number, if needed: | |
inBinary = inBinary.rjust(image_width * image_height, '0') | |
for y in range(image_height): | |
for x in range(image_width): | |
turtle.goto(left + (x * pixel_size), top - (y * pixel_size)) | |
if inBinary[y * image_width + x] == '1': # (!) Try switching this to '0'. | |
turtle.begin_fill() | |
turtle.setheading(0) | |
turtle.forward(pixel_size) # draw top of the box | |
turtle.right(90) | |
turtle.forward(pixel_size) # draw right edge of the box | |
turtle.right(90) | |
turtle.forward(pixel_size) # draw bottom of the box | |
turtle.right(90) | |
turtle.forward(pixel_size) # draw left edge of the box | |
turtle.end_fill() # fill in the box | |
try: | |
main() | |
except turtle.Terminator: | |
pass # Do nothing when the turtle window is closed. |
This file contains 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
"""Multiplication Table, by Al Sweigart [email protected] | |
Print a multiplication table. | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, beginner, math""" | |
__version__ = 0 | |
print('Multiplication Table, by Al Sweigart [email protected]') | |
# Print the horizontal number labels: | |
print(' | 0 1 2 3 4 5 6 7 8 9 10 11 12') | |
print('--+---------------------------------------------------') | |
# Display each row of products: | |
for number1 in range(0, 13): | |
# Print the vertical numbers labels: | |
print(str(number1).rjust(2), end='') | |
# Print a separating bar: | |
print('|', end='') | |
for number2 in range(0, 13): | |
# Print the product: | |
print(str(number1 * number2).rjust(3), end=' ') | |
print() # Finish the row by printing a newline. |
This file contains 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
"""Multiplicative Persistence, by Al Sweigart [email protected] | |
A fun math challenge. | |
For more information about this topic, see https://youtu.be/Wim9WJeDTHQ | |
This and other games are available at https://nostarch.com/XX | |
Tags: tiny, math""" | |
__version__ = 0 | |
import time, sys | |
print('Multiplicative Persistence Game') | |
print('By Al Sweigart [email protected]') | |
print() | |
while True: # Main program loop. | |
print('Try to get the longest multiplicative persistence chain!') | |
print('(Start with 277777788888899, which has the longest known') | |
print('chain length.)') | |
while True: # Keep asking until the player enters a number. | |
print('Enter a number (or "QUIT" to quit):') | |
try: | |
response = input('> ') | |
if response.upper().startswith('Q'): | |
sys.exit() | |
number = int(response) | |
except ValueError: | |
continue # If the user entered a non-integer, ask again. | |
break | |
chainLength = 0 | |
while number > 9: # Loop as long as number is 2 or more digits. | |
chainLength += 1 | |
print(number, end='', flush=True) | |
time.sleep(0.2) | |
print(' -> ', end='', flush=True) | |
time.sleep(0.2) | |
print('*'.join(list(str(number))), end='', flush=True) | |
time.sleep(0.2) | |
print(' = ', end='', flush=True) | |
time.sleep(0.2) | |
# Calculate the next number in the multiplicative persistence | |
# chain by multiplying all of the digits in the number: | |
product = 1 | |
for digit in str(number): | |
product *= int(digit) | |
number = product | |
print(number, flush=True) | |
time.sleep(0.6) | |
print(number) | |
print('Length of', response, 'chain:', chainLength) | |
print() |
This file contains 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
import random | |
import time | |
# сорі за суржик | |
phrases = [ | |
"{}, вставай!", | |
"все пропало, {}!", | |
"ми все уронілі, {}!", | |
"{}, ти спіш?", | |
] | |
cat1 = r""" | |
|\---/| | |
| o_o | | |
\_^_/ | |
""" | |
cat2 = r""" | |
/\_/\ | |
( o.o ) | |
> ^ < | |
""" | |
cat3 = r""" | |
|\__/,| (`\ | |
_.|o o |_ ) ) | |
-(((---(((-------- | |
""" | |
def prepare_cat(cat): | |
""" | |
Перетворити картинку котика в список рядків. І зробити картинку прямокутною, | |
додавши в кінці кожного рядку прогаликів | |
In[]: cat = r''' | |
|\__/| | |
| OO |._ | |
-(((---(((- | |
''' | |
In[]: prepare_cat(cat) | |
Out[]: [ | |
'|\__/| ', | |
'| OO |._ ', | |
'-(((---(((-' | |
] | |
""" | |
lines = cat.split("\n") | |
max_length = max([ len(line) for line in lines ]) | |
return [ x.ljust(max_length) for x in lines[1:-1]] | |
def symbol_reverse(sym): | |
""" | |
Віддзеркалити символ. Це потрібно для відзеркалення картинки котика. | |
Наприклад, якщо котик виглядає як | |
cat = r'|\_/| ' | |
то при розвертанні рядка нахилені символи / та \ поміняються місцями: | |
r'|\_/| '[::-1] == r' |/_\|' | |
От для цих несиметричних символів і потрібна ця функція. | |
""" | |
if sym == '(': | |
return ')' | |
elif sym == ')': | |
return '(' | |
elif sym == '/': | |
return '\\' | |
elif sym == '\\': | |
return '/' | |
elif sym == '>': | |
return '<' | |
elif sym == '<': | |
return '>' | |
else: | |
return sym | |
def ascii_mirror(cat_lines, offset=0): | |
""" | |
Віддзеркалити всього котика. Параметр offset автоматично посуне котика | |
вліво. | |
""" | |
result = [] | |
for line in cat_lines: | |
line_reverse = line[::-1] | |
line_correct = ''.join([ symbol_reverse(x) for x in line_reverse ]) | |
result.append(line_correct.rjust(offset)) | |
return result | |
# нормальні котики | |
all_cats = [ prepare_cat(cat) for cat in [cat1, cat2] ] | |
# дзеркальні котики | |
all_cats += [ ascii_mirror(prepare_cat(cat)) for cat in [cat3] ] | |
try: | |
while True: | |
time.sleep(0.7) | |
cat = random.choice(all_cats) | |
phrase = random.choice(phrases) | |
name = "Наташ" | |
if random.random() < 0.5: # картинка справа | |
print("\n".join(cat)) | |
print(phrase.format(name)) | |
else: # картинка зліва | |
cat = ascii_mirror(cat, offset=60) | |
print("\n".join(cat)) | |
print(phrase.format(name).rjust(60)) | |
except KeyboardInterrupt: | |
pass |
This file contains 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
import turtle | |
import math | |
import itertools | |
import time | |
import random | |
turtle.tracer(0, 0) | |
time_start = time.time() | |
def beat(size): | |
turtle.pendown() | |
turtle.color("red") | |
turtle.pensize(4) | |
turtle.forward(size) | |
turtle.penup() | |
def music_spectrum(angle, music): | |
return abs(50 * (math.sin(2*(time.time() - time_start))-1) + random.randrange(100)) | |
def draw(radius, music=None, N=60): | |
turtle.penup() | |
turtle.goto(0, radius) | |
for i in range(N+1): | |
angle = i * 360 / N | |
x = radius * math.cos(angle*math.pi/180 + math.pi/2) | |
y = radius * math.sin(angle*math.pi/180 + math.pi/2) | |
turtle.goto(x, y) | |
turtle.setheading(angle + 90) | |
spec_size = music_spectrum(angle, music) | |
beat(spec_size) | |
turtle.color("black") | |
turtle.goto(-90, 00) | |
turtle.write("Turtle", font=("Courier", 35, "bold")) | |
turtle.goto(-100, -40) | |
turtle.write(" Nation", font=("Courier", 35, "bold")) | |
class ParticleSystem: | |
def reset_particle(self, t): | |
t.penup() | |
t.goto(0, 0) | |
t.shape("circle") | |
t.shapesize(random.random()) | |
t.color("#BBBBBB") | |
t.setheading(random.randrange(361)) | |
t._spd = random.randrange(10, 50) | |
def __init__(self, num): | |
self.pool = [turtle.Turtle() for _ in range(num)] | |
self.previous_time = time.time() - 10 | |
for t in self.pool: | |
self.reset_particle(t) | |
def update(self, time): | |
dt = time - self.previous_time | |
self.previous_time = time | |
for t in self.pool: | |
t.forward(dt*t._spd) | |
if t.xcor()**2 + t.ycor()**2 > 400**2: | |
self.reset_particle(t) | |
ps = ParticleSystem(40) | |
i = 0 | |
while True: | |
ps.update(time.time()) | |
if i % 2: | |
turtle.clear() | |
draw(100) | |
turtle.update() | |
time.sleep(0.05) | |
i += 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment