fast written langtons ant implementation
This commit is contained in:
parent
a790f78cfd
commit
d3caa4ded7
296
langton/Main.py
Normal file
296
langton/Main.py
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import pygame
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
from OpenGL.GL import *
|
||||||
|
from OpenGL.GLU import *
|
||||||
|
from pygame.locals import *
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
video_flags = OPENGL | HWSURFACE | DOUBLEBUF
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
dinfo = pygame.display.Info();
|
||||||
|
|
||||||
|
window_w = 1600
|
||||||
|
window_h = 900
|
||||||
|
|
||||||
|
livingSpaceWidth = 64
|
||||||
|
livingSpaceHeight = 36
|
||||||
|
|
||||||
|
creatureW = window_w/(livingSpaceWidth)
|
||||||
|
creatureH = window_h/(livingSpaceHeight)
|
||||||
|
|
||||||
|
FPS = 60
|
||||||
|
|
||||||
|
NORTH = 0
|
||||||
|
EAST = 1
|
||||||
|
SOUTH = 2
|
||||||
|
WEST = 3
|
||||||
|
|
||||||
|
livingSpace = []
|
||||||
|
|
||||||
|
update_queue = []
|
||||||
|
old_update_queue = []
|
||||||
|
|
||||||
|
draw_buffer = []
|
||||||
|
draw_buffer_old = []
|
||||||
|
|
||||||
|
antPosition = (livingSpaceWidth // 2, livingSpaceHeight // 2)
|
||||||
|
antRotation = NORTH
|
||||||
|
|
||||||
|
def resize(shape):
|
||||||
|
width, height = shape
|
||||||
|
if height == 0:
|
||||||
|
height = 1
|
||||||
|
glViewport(0, 0, width, height)
|
||||||
|
glMatrixMode(GL_PROJECTION)
|
||||||
|
glLoadIdentity()
|
||||||
|
glOrtho(0.0, livingSpaceWidth * creatureW, livingSpaceHeight * creatureH, 0.0, -6.0, 0.0)
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
glLoadIdentity()
|
||||||
|
|
||||||
|
def init():
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
def initLivingSpace():
|
||||||
|
for x in range(livingSpaceWidth):
|
||||||
|
livingSpace.append([])
|
||||||
|
for y in range(livingSpaceHeight):
|
||||||
|
livingSpace[x].append(100)
|
||||||
|
draw_buffer.append((x,y))
|
||||||
|
|
||||||
|
def isAlive(x,y):
|
||||||
|
return livingSpace[x][y] > 101 # epsilon = 1
|
||||||
|
|
||||||
|
def draw():
|
||||||
|
global draw_buffer
|
||||||
|
global draw_buffer_old
|
||||||
|
#glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||||
|
glLoadIdentity()
|
||||||
|
glTranslatef(0.0,0.0,3.0)
|
||||||
|
|
||||||
|
|
||||||
|
glBegin(GL_QUADS)
|
||||||
|
|
||||||
|
for column,row in draw_buffer + draw_buffer_old:
|
||||||
|
if livingSpace[column][row] != 0:
|
||||||
|
health = float(float(livingSpace[column][row])/1000.0)
|
||||||
|
|
||||||
|
glColor4f(health*(float(column)/float(livingSpaceWidth)),
|
||||||
|
health*(float(livingSpaceWidth-column)/float(livingSpaceWidth)),
|
||||||
|
health*(float(row)/float(livingSpaceHeight)),1.0)
|
||||||
|
x = column * creatureW
|
||||||
|
y = row * creatureH
|
||||||
|
|
||||||
|
glVertex3f(x,y,0.0)
|
||||||
|
glVertex3f(x + creatureW-1.0,y,0.0)
|
||||||
|
glVertex3f(x+creatureW-1,y+creatureH-1,0.0)
|
||||||
|
glVertex3f(x,y+creatureH-1,0.0)
|
||||||
|
|
||||||
|
# draw langton's ant quad:
|
||||||
|
x = antPosition[0] * creatureW
|
||||||
|
y = antPosition[1] * creatureH
|
||||||
|
|
||||||
|
offX = creatureW / 2
|
||||||
|
offY = creatureH / 2
|
||||||
|
|
||||||
|
glColor4f(1,1,1,0.5)
|
||||||
|
|
||||||
|
glVertex3f(x + offX, y, 0.0)
|
||||||
|
glVertex3f(x + creatureW - 1, y + offY, 0.0)
|
||||||
|
glVertex3f(x + offX, y + creatureH - 1, 0.0)
|
||||||
|
glVertex3f(x, y + offX - 1, 0.0)
|
||||||
|
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
draw_buffer_old = draw_buffer
|
||||||
|
draw_buffer = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getNeighborCount(x,y):
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
xpn = (x + 1) % livingSpaceWidth
|
||||||
|
ypn = (y + 1) % livingSpaceHeight
|
||||||
|
|
||||||
|
count += isAlive(x,ypn)
|
||||||
|
count += isAlive(xpn,ypn)
|
||||||
|
count += isAlive(xpn,y)
|
||||||
|
count += isAlive(xpn,y-1)
|
||||||
|
count += isAlive(x,y-1)
|
||||||
|
count += isAlive(x-1,y-1)
|
||||||
|
count += isAlive(x-1,y)
|
||||||
|
count += isAlive(x-1,ypn)
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
def calculateNextGeneration():
|
||||||
|
neighborCount = []
|
||||||
|
for column in range(livingSpaceWidth):
|
||||||
|
neighborCount.append([])
|
||||||
|
for row in range(livingSpaceHeight):
|
||||||
|
neighborCount[column].append(getNeighborCount(column,row))
|
||||||
|
|
||||||
|
for column in range(livingSpaceWidth):
|
||||||
|
for row in range(livingSpaceHeight):
|
||||||
|
if 2 <= neighborCount[column][row] <= 3:
|
||||||
|
if neighborCount[column][row] == 3:
|
||||||
|
livingSpace[column][row] = 1000
|
||||||
|
if not isAlive(column,row):
|
||||||
|
livingSpace[column][row] = float(livingSpace[column][row])/1.2
|
||||||
|
|
||||||
|
else:
|
||||||
|
livingSpace[column][row] = float(livingSpace[column][row])/1.2
|
||||||
|
|
||||||
|
if livingSpace[column][row] < 20:
|
||||||
|
livingSpace[column][row] = 0
|
||||||
|
|
||||||
|
|
||||||
|
def activate(i,j):
|
||||||
|
livingSpace[i][j] = 1000
|
||||||
|
update_queue.append((i,j))
|
||||||
|
|
||||||
|
def deactivate(i,j):
|
||||||
|
livingSpace[i][j] = 100
|
||||||
|
update_queue.append((i,j))
|
||||||
|
|
||||||
|
def update_field():
|
||||||
|
global old_update_queue
|
||||||
|
global update_queue
|
||||||
|
|
||||||
|
old_update_queue = update_queue
|
||||||
|
update_queue = []
|
||||||
|
for i,j in old_update_queue:
|
||||||
|
draw_buffer.append((i,j))
|
||||||
|
if livingSpace[i][j] > 600:
|
||||||
|
livingSpace[i][j] = float(livingSpace[i][j]) * 0.98
|
||||||
|
if livingSpace[i][j] < 600:
|
||||||
|
livingSpace[i][j] = 600
|
||||||
|
update_queue.append((i,j))
|
||||||
|
|
||||||
|
def move_ant():
|
||||||
|
global antPosition
|
||||||
|
global antRotation
|
||||||
|
|
||||||
|
# switch cell
|
||||||
|
if isAlive(antPosition[0], antPosition[1]):
|
||||||
|
deactivate(antPosition[0], antPosition[1])
|
||||||
|
else:
|
||||||
|
activate(antPosition[0], antPosition[1])
|
||||||
|
|
||||||
|
# turn
|
||||||
|
if isAlive(antPosition[0], antPosition[1]):
|
||||||
|
# turn right
|
||||||
|
antRotation = (antRotation + 1) % 4
|
||||||
|
else:
|
||||||
|
# turn left
|
||||||
|
antRotation = (antRotation + 3) % 4
|
||||||
|
|
||||||
|
# move on step:
|
||||||
|
dx = 0
|
||||||
|
dy = 0
|
||||||
|
if antRotation == NORTH:
|
||||||
|
dy = -1
|
||||||
|
elif antRotation == SOUTH:
|
||||||
|
dy = +1
|
||||||
|
elif antRotation == EAST:
|
||||||
|
dx = +1
|
||||||
|
elif antRotation == WEST:
|
||||||
|
dx = -1
|
||||||
|
|
||||||
|
# check bounds:
|
||||||
|
if antPosition[0] == 0 and dx < 0:
|
||||||
|
dx = 0
|
||||||
|
if antPosition[0] == livingSpaceWidth - 1 and dx > 0:
|
||||||
|
dx = 0
|
||||||
|
if antPosition[1] == 0 and dy < 0:
|
||||||
|
dy = 0
|
||||||
|
if antPosition[1] == livingSpaceHeight - 1 and dy > 0:
|
||||||
|
dy = 0
|
||||||
|
|
||||||
|
# finally move:
|
||||||
|
antPosition = (antPosition[0] + dx, antPosition[1] + dy)
|
||||||
|
update_queue.append(antPosition)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def update_ant():
|
||||||
|
global antPosition
|
||||||
|
global antRotation
|
||||||
|
|
||||||
|
move_ant()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
global livingSpaceWidth
|
||||||
|
global livingSpaceHeight
|
||||||
|
global creatureW
|
||||||
|
global creatureH
|
||||||
|
global antPosition
|
||||||
|
|
||||||
|
# parsing args:
|
||||||
|
parser = argparse.ArgumentParser(description="langton's ant")
|
||||||
|
parser.add_argument('--steps', dest='steps', default = 20 , help='steps per second')
|
||||||
|
parser.add_argument('--w', dest='w', default = livingSpaceWidth, help = 'field width')
|
||||||
|
parser.add_argument('--h', dest='h', default=livingSpaceHeight, help = 'field height')
|
||||||
|
parser.add_argument('--calc', dest='calc', default=0, help='calculate steps and only display result')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
steps_per_sec = int(args.steps)
|
||||||
|
livingSpaceWidth = int(args.w)
|
||||||
|
livingSpaceHeight = int(args.h)
|
||||||
|
calc = int(args.calc)
|
||||||
|
|
||||||
|
creatureW = window_w / (livingSpaceWidth)
|
||||||
|
creatureH = window_h / (livingSpaceHeight)
|
||||||
|
|
||||||
|
antPosition = (livingSpaceWidth // 2, livingSpaceHeight // 2)
|
||||||
|
|
||||||
|
|
||||||
|
pygame.display.set_mode((window_w,window_h),video_flags)
|
||||||
|
|
||||||
|
initLivingSpace()
|
||||||
|
resize((window_w, window_h))
|
||||||
|
init()
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
frames = 0
|
||||||
|
counter = 0
|
||||||
|
logic_frame_pause = FPS / steps_per_sec
|
||||||
|
|
||||||
|
if (calc > 0):
|
||||||
|
for i in range(calc):
|
||||||
|
update_ant();
|
||||||
|
|
||||||
|
#main loop:
|
||||||
|
while True:
|
||||||
|
|
||||||
|
|
||||||
|
ticktime = clock.tick(FPS)
|
||||||
|
#print ticktime
|
||||||
|
event = pygame.event.poll()
|
||||||
|
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
|
||||||
|
break
|
||||||
|
|
||||||
|
update_field();
|
||||||
|
draw()
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
if (calc == 0):
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
if counter > logic_frame_pause:
|
||||||
|
update_ant()
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user