# -*- coding: utf-8 -*-
# This file is called flora.py and the release is 2
# Wolf Mathwig wrote this stuff and uploaded it to the internet on 2012-07-02
# It is a submission for the first NeoGAF Weekly Recreational Programming Challenge
#
# It was a Monday. Windows prompted a change.
#
# This is is a script developed for Python 2.6.7 on Mac OS X
# It needs a terminal that supports UTF-8 and ANSI terminal colors
#
# The output only looks perfect if the font you're using includes block characters,
# and has perfect spacing (this is not necessarily the case for arbitrary reasons)
#
# Optimal font properties:
# Menlo Regular 14 pt.
# Distance between letters: 1
# Distance between rows: 0.81
#
# If something breaks, hit me: wolf.mathwig@googlemail.com
import random, sys, os, time
random.seed()
height, width = 0,0
bad_start = 0
try:
height, width = os.popen('stty size', 'r').read().split()
height = int(height)
width = int(width)
except:
try:
from ctypes import windll, create_string_buffer
# stdin handle is -10
# stdout handle is -11
# stderr handle is -12
h = windll.kernel32.GetStdHandle(-12)
csbi = create_string_buffer(22)
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
if res:
import struct
(bufx, bufy, curx, cury, wattr,
left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
width = right - left + 1
height = bottom - top + 1
else:
width, height = 80, 25 # can't determine actual size - return default values
except:
print "I cannot determine your console size. Please provide your console dimensions in characters."
try:
width = int(raw_input('Width: '))
height = int(raw_input('Height: '))
except:
bad_start = 1
print "You provided input I cannot work with. I will use 20x20"
width = 20
height = 20
if not bad_start:
print "Console dimensions: "+str(width)+"x"+str(height)
if int(height)<15:
print "Console height is 15 characters minimum. Sorry."
sys.exit()
if bad_start:
print "I will continue working in 5 seconds."
time.sleep(5)
canvas = {'dx': ((width-1)/2*2)*2, 'dy': ((height-3)/2*2)*2, 'pic':[]}
color = {'dx':canvas['dx']/2, 'dy':canvas['dy']/2, 'pic':[]}
sleep_time = 0.05
global_counter = 0
runs = 0
max_y = canvas['dy']-2
if max_y<0:
max_y = 2
raster = { "0000":" ",
"0001":"▗",
"0010":"▖",
"0011":"▄",
"0100":"▝",
"0101":"▐",
"0110":"▞",
"0111":"▟",
"1000":"▘",
"1001":"▚",
"1010":"▌",
"1011":"▙",
"1100":"▀",
"1101":"▜",
"1110":"▛",
"1111":"█",
}
# Raster ANSI escape colours
bl = '\033[30m'
blu = '\033[44m'
b1 = '\033[31m'
b2 = '\033[91m'
g1 = '\033[32m'
g2 = '\033[92m'
p1 = '\033[35m'
p2 = '\033[95m'
# Nulling the canvas data structure
for y in range(0, canvas['dy']):
canvas['pic'].append([])
for x in range(0, canvas['dx']):
pp = 0
# Frame with random pixels (UNUSED because scrolling makes it look weird)
#if x == 0 or x == canvas['dx']-1 or y == 0 or y == canvas['dy']-1:
# pp = random.randint(0,1)
canvas['pic'][y].append(pp)
# Nulling color to blue background
for y in range(0, color['dy']):
color['pic'].append([])
for x in range(0, color['dx']):
color['pic'][y].append(blu)
# Hint: We grow downwards, display flipped
# Decode data structure and display it
def pic_decode():
global global_counter, runs
i = 0
p = 0
c = 0
outp = ""
# Scrolling driver
init_i = global_counter%color['dx']
trans = []
while p < len(canvas['pic']):
trans.append([])
i = 0
while i < len(canvas['pic'][p]):
trans[p/2].append(str(canvas['pic'][p][i])+str(canvas['pic'][p][i+1])+\
str(canvas['pic'][p+1][i])+str(canvas['pic'][p+1][i+1]))
i += 2
p += 2
if os.name == "posix":
# Unix/Linux/MacOS/BSD/etc
os.system('clear')
#print "cls"
elif os.name in ("nt", "dos", "ce"):
# DOS/Windows
os.system('CLS')
# Fill and output data and color
for f1 in range(0, len(trans)):
print outp
outp = ""
i = init_i
# Scroll: Left portion is what's right of i
for f2 in range(i, len(trans[f1])):
outp += color['pic'][f1][f2] + raster[trans[f1][f2]]
# Scroll: Right portion is what's left of i
for f2 in range(0, init_i):
outp += color['pic'][f1][f2] + raster[trans[f1][f2]]
# Stats barf
print "\033[0mSTEP "+ '_'*(6-len(str(global_counter))) +str(global_counter)+\
" RUNS "+str(runs)
global_counter += 1
time.sleep(sleep_time)
# UNUSED -- Randomized pixels
def randomized(runs):
for i in range(0,runs):
for y in range(0, canvas['dy']):
for x in range(0, canvas['dx']):
canvas['pic'][y][x] = random.randint(0,1)
pic_decode()
# Determine slope and y at x=0
def linfunc(start,end):
x = end['x'] - start['x']
y = end['y'] - start['y']
slope = 0
b = 0
try:
slope = y / x
b = start['y']-slope*start['x']
except:
# Division by zero
slope = 0
b = 0
return {'slope':slope, 'b':b}
# Draw a line
# col is a toggle between grass and tree
# hint is for determining which line in a wider segment is drawn
def line(start, end, col, hint=0):
global max_y, runs
slope = linfunc(start, end)
b = slope['b']
slope = slope['slope']
x = start['x']
y = start['y']
if not slope:
return start
for j in range(start['y'], end['y']):
if j > (max_y-random.randint(0, 15)):
#print "exceed"
break
try:
old_x = x
old_y = y
x = (j-b)/slope
y = j-1
try:
canvas['pic'][len(canvas['pic'])-y][x] = 1
if col:
if (end['y']-y) >2:
c_pick = g1
else:
c_pick = g2
else:
if not random.randint(0, 30):
if random.randint(0,1):
c_pick = p2
else:
c_pick = p1
else:
if not hint:
c_pick = b1
if hint==1:
c_pick = b2
if hint==-1:
c_pick = bl
color['pic'][(len(canvas['pic'])-y)/2][x/2] = c_pick
except:
#print str(x)+"."+str(y)+": out of array"
x = old_x
y = old_y
break
except:
#print "div by 0"
break
if random.randint(0,runs)==0:
pic_decode()
# We return the last-drawn position so that segments can continue there
return {'x':x, 'y':y}
# Draw a number of grass leaves with a height range
def grass(stems, height):
for i in range(0, stems):
c_height = random.randint(height['min'], height['max'])
x = random.randint(0, canvas['dx']-2)
if x > ((height['min']+ height['max']) / 2) and random.randint(0, 5) > 1:
c_height = random.randint(height['min'],\
height['min'] + (height['min']+ height['max']) / 2)
target = random.randint(-c_height, c_height)
line({'x':x, 'y':2}, {'x':x+target, 'y':c_height}, 1)
# Draw a flower stem segment
def flowerstem_segment(start, end, thickness, overdraw):
result = {'x':start['x'], 'y':start['y']}
slope = linfunc(start, end)
x = start['x']
for t in range(0, thickness):
factor = -1
c_t = t
result = line(
{'x':start['x'], 'y':start['y']},
{'x':end['x'], 'y':end['y']}, 0,1)
while c_t > 0:
factor *= -1
line(
{'x':start['x'] +c_t*factor, 'y':start['y']},
{'x':end['x'] +c_t*factor, 'y':end['y']}, 0)
c_t -= 1
result['y'] += 1
if not t:
continue
line({'x':start['x']+t, 'y':start['y']}, {'x':end['x']+t, 'y':end['y']}, 0)
# SHADOW
line({'x':start['x']+t+1, 'y':start['y']}, {'x':end['x']+t+1, 'y':end['y']}, 0, -1)
return result
# Draw a flower stem, which itself consists of flower stem segments
# variance determines height and width variance for segments
# overdraw is unused
def flowerstem(variance, thickness, overdraw, height, segments, pos={'x':-1,'y':-1}):
if not segments:
segments = random.randint(3, 15)
s = 0
if pos['x']==-1:
pos = {'x':random.randint(thickness, canvas['dx']-thickness), 'y':0}
while pos['y']<height and s < segments:
grass(random.randint(0,1), {'min':2, 'max':canvas['dy']/random.randint(8,12)})
c_variance_x = random.randint(0, variance)
c_variance_y = random.randint(1, (int(variance*0.6)%12)+2)
end = { 'x':random.randint(pos['x']-c_variance_x/2, pos['x']+c_variance_x),
'y':random.randint(pos['y']+1, pos['y']+c_variance_y)}
pos = flowerstem_segment(pos, end, thickness, overdraw)
if thickness > 1 and not random.randint(0,1):
c_thickness = thickness - 1
flowerstem(variance*2, c_thickness, overdraw, height, s, {'x':pos['x'],'y':pos['y']})
s += 1
# Draw a bunch of flowers
def flowers(stems):
global max_y
for i in range(0, stems):
flowerstem( random.randint(3, 8), # variance
random.randint(2, 6), # thickness
random.randint(0, 1), # overdraw
random.randint(10, max_y), # height
random.randint(0, 10)) # segments
# How many times do we repeat the basic thing?
runs = random.randint(3, 100)
while runs > 0:
try:
if random.randint(0,2):
grass(random.randint(10,35), {'min':2, 'max':canvas['dy']/random.randint(4,9)})
if random.randint(0,1):
flowers(random.randint(1,2))
except KeyboardInterrupt:
runs = 0
runs -= 1
pic_decode()
# We have to reset the terminal color
print "\033[0m"