I'd found myself getting back into programming, and had recalled a fascinating little 'game' many might know of. I'm a very inexperienced programmer, and I know this is sloppy, but I'd wanted to make something that had a sort of pseudo-cellular life kind of behavior.
There's no real pattern to their movement, but it does create a quite mesmerizing effect. All it does is generate a 'turtle' that wanders around turning tiles to the 'on' state until it dies after about 10 steps. If it encounters an 'on' tile, the turtle creates a new turtle in an adjacent space.
I'm well aware I have this really horribly optimized, but I'm hoping to expand upon it and probably just rewrite it more sanely. I used a little package called 'tdl' for ASCII graphics because I enjoy them and I really don't understand matplotlab animation at all or animation in general.
Controls:
- Press 2 or 3 (not numpad) to add a new turtle value at a random position
- Press Enter to increment one 'step'
- Press c to clear the board
- Press p to print the current population to the console
This is my bag of colors. It'll ask if you want to download upon opening the link.
This is the font used:
from random import randint #Woot! My turtles live life with no aim! Just livin on psudo-random whims XD
import numpy as np #You all know what this is, but I barely do
import tdl #A neat little package for handling ascii graphics. Just using it because it's very simple and fun.... and I don't really understand matplotlib animations....
import colors #Just a big bag of color values
SCREEN_WIDTH = 80
SCREEN_HEIGHT = 80
FONTSET = 'arial10x10.png'
LIMIT_FPS = 20
MAP_WIDTH = 100
MAP_HEIGHT = 80
BOARD = np.zeros( (MAP_WIDTH, MAP_HEIGHT) )
class TurtleBrain():
def __init__(self, pattern):
self.flag = True
self.pattern = pattern
def process(self):
"""Handles the 'steps' for each turtle value
I did it this way so that I could later decide to add new values with different movements and behaviors
or have several different types of life on the board at once, if I so choose to impliment them
"""
while self.pattern == 'original':
if self.flag:
self.move_twos()
self.flag = (not self.flag)
break
else:
self.move_threes()
self.flag = (not self.flag)
break
def move_twos(self):
"""
I'm sure there are so many better ways to do this:
Form lists of coordinates and data
Make a dictionary of coordinate keys with data values?
Sparse arrays?
"""
#Find every 2 turtle
for y in range(MAP_HEIGHT-1):
for x in range(MAP_WIDTH-1):
cell = BOARD[x, y]
if 2.0 <= cell <= 2.2:
failed = True
while True:
dx = randint(-1, 1)
dy = randint(-1, 1)
new_x = x + dx
new_y = y + dy
if dx == 0 and dy == 0:
continue
else:
failed = False
break
new_cell = BOARD[int(new_x), int(new_y)]
if new_cell == 1.0:
BOARD[int(new_x), int(new_y)] = BOARD[int(x), int(y)] + 1
BOARD[int(x), int(y)] = 0.0
self.make_child(new_x, new_y)
elif new_cell == 0.0:
BOARD[int(new_x), int(new_y)] = BOARD[int(x), int(y)] + 1
BOARD[int(x), int(y)] = 1.0
else:
BOARD[int(new_x), int(new_y)] += .01
def move_threes(self):
#find every 3 turtle
for y in range(MAP_HEIGHT-1):
for x in range(MAP_WIDTH-1):
cell = BOARD[x, y]
if 3.0 <= cell <= 3.2:
failed = True
while True:
dx = randint(-1, 1)
dy = randint(-1, 1)
new_x = x + dx
new_y = y + dy
if dx == 0 and dy == 0:
continue
else:
failed = False
break
new_cell = BOARD[int(new_x), int(new_y)]
if new_cell == 1.0:
BOARD[int(new_x), int(new_y)] = BOARD[int(x), int(y)] - 1
BOARD[int(x), int(y)] = 0.0
self.make_child(new_x, new_y)
elif new_cell == 0.0:
BOARD[int(new_x), int(new_y)] = BOARD[int(x), int(y)] - 1
BOARD[int(x), int(y)] = 1.0
else:
BOARD[int(new_x), int(new_y)] += .01
def move_fours(self):
"""Planned as a predator type to cull the 2 and 3 hoards mwuahaha"""
for x in BOARD:
for y in BOARD[x]:
cell = BOARD[x, y]
if 4.0 <= cell <= 4.2:
pass
def make_child(self, px, py):
failed = True
while True:
child_x = px + randint(-1, 1)
child_y = py + randint(-1, 1)
if BOARD[int(child_x), int(child_y)] == 0.0 or BOARD[int(child_x), int(child_y)] == 1.0:
failed = False
break
if not failed:
BOARD[int(child_x), int(child_y)] = 2.0
#print("I made a child at (%d, %d)" % (child_x, child_y))
class TimeStep():
def __init__(self, age_value):
self.age_value = age_value
def process(self):
for y in range(MAP_HEIGHT):
for x in range(MAP_WIDTH):
if 2.0 <= BOARD[x, y] <= 3.1:
#print("Test?")
BOARD[x, y] += self.age_value
#print(str(BOARD[x, y]) + " at (%d, %d)" % (x, y))
if 2.1 <= BOARD[x, y] <= 2.9 or 3.1 <= BOARD[x, y] <= 3.9:
BOARD[x, y] = 0.0
#print("Turtle at (%d, %d) died" % (x, y))
pass
class RenderProcessor():
def __init__(self, _con, _root):
super().__init__()
self._con = _con
self._root = _root
def process(self):
for y in range(MAP_HEIGHT):
for x in range(MAP_WIDTH):
if BOARD[x, y] == 0.0:
self._con.draw_char(x, y, '#', fg=colors.dark_grey, bg=colors.darker_grey)
#elif 2.0 <= BOARD[x, y] <= 2.99 or 3.0 <= BOARD[x, y] <= 3.99:
# self._con.draw_char(x, y, '@', fg=colors.dark_green, bg=colors.black)
elif 2.0 <= BOARD[x, y] <= 2.01 or 3.0 <= BOARD[x, y] <= 3.01:
self._con.draw_char(x, y, '0', fg=colors.dark_green, bg=colors.black)
elif 2.01 <= BOARD[x, y] <= 2.02 or 3.01 <= BOARD[x, y] <= 3.02:
self._con.draw_char(x, y, '1', fg=colors.dark_lime, bg=colors.black)
elif 2.02 <= BOARD[x, y] <= 2.03 or 3.02 <= BOARD[x, y] <= 3.03:
self._con.draw_char(x, y, '2', fg=colors.dark_red, bg=colors.black)
elif 2.03 <= BOARD[x, y] <= 2.04 or 3.03 <= BOARD[x, y] <= 3.04:
self._con.draw_char(x, y, '3', fg=colors.dark_blue, bg=colors.black)
elif 2.04 <= BOARD[x, y] <= 2.05 or 3.04 <= BOARD[x, y] <= 3.05:
self._con.draw_char(x, y, '4', fg=colors.dark_purple, bg=colors.black)
elif 2.05 <= BOARD[x, y] <= 2.06 or 3.05 <= BOARD[x, y] <= 3.06:
self._con.draw_char(x, y, '5', fg=colors.dark_violet, bg=colors.black)
elif 2.06 <= BOARD[x, y] <= 2.07 or 3.06 <= BOARD[x, y] <= 3.07:
self._con.draw_char(x, y, '6', fg=colors.dark_sky, bg=colors.black)
elif 2.07 <= BOARD[x, y] <= 2.08 or 3.07 <= BOARD[x, y] <= 3.08:
self._con.draw_char(x, y, '7', fg=colors.dark_cyan, bg=colors.black)
elif 2.08 <= BOARD[x, y] <= 2.09 or 3.08 <= BOARD[x, y] <= 3.09:
self._con.draw_char(x, y, '8', fg=colors.dark_cyan, bg=colors.black)
elif 2.09 <= BOARD[x, y] < 2.1 or 3.09 <= BOARD[x, y] <= 3.1:
self._con.draw_char(x, y, '9', fg=colors.darkest_pink, bg=colors.black)
else:
self._con.draw_char(x, y, '#', fg=colors.darker_green, bg=colors.dark_green)
self._root.blit(self._con, 0, 0, MAP_WIDTH, MAP_HEIGHT, 0, 0)
tdl.flush()
##################################################################
tdl.set_font(FONTSET, greyscale=True, altLayout=True)
root = tdl.init(SCREEN_WIDTH, SCREEN_HEIGHT, title="TURTLES",
fullscreen=False)
tdl.setFPS(LIMIT_FPS)
con = tdl.Console(MAP_HEIGHT, MAP_WIDTH)
##################################################################
def count_turts():
total = 0
for y in range(MAP_HEIGHT - 1):
for x in range(MAP_WIDTH - 1):
cell = BOARD[x, y]
if 2.0 <= cell <= 2.12 or 3.0 <= cell <= 3.12:
total += 1
print("There are %d total turtles" % (total))
BOARD[50, 50] = 2.0
turtlebrain = TurtleBrain(pattern='original')
timeprocessor = TimeStep(.01)
renderprocessor = RenderProcessor(con, root)
con.clear()
def handle_keys():
user_input = tdl.event.key_wait()
if user_input.key == 'ESCAPE':
return 'exit'
if user_input.key == 'ENTER':
return 'go'
if user_input.char == '2':
return user_input.char
if user_input.char == '3':
return user_input.char
if user_input.char == 'c':
return 'clear'
if user_input.char == 'p':
return user_input.char
if user_input.key == 'F11':
return user_input.key
renderprocessor.process()
def add_turtle(turt):
BOARD[randint(0, MAP_WIDTH - 1), randint(0, MAP_HEIGHT - 1)] = 2.0
while not tdl.event.is_window_closed():
player_input = handle_keys()
if player_input == 'go':
turtlebrain.process()
timeprocessor.process()
renderprocessor.process()
elif player_input == 'exit':
break
elif player_input == 'F11':
tdl.set_fullscreen(not tdl.get_fullscreen())
pass
elif player_input == '2':
add_turtle(2)
elif player_input == '3':
add_turtle(3)
elif player_input == 'p':
count_turts()
elif player_input == 'clear':
BOARD = np.zeros( (MAP_WIDTH, MAP_HEIGHT) )
renderprocessor.process()