From d3caa4ded76e0be36355bd064e0145b05286ee49 Mon Sep 17 00:00:00 2001 From: Jonas Weinz Date: Sun, 7 May 2017 18:47:32 +0200 Subject: [PATCH] fast written langtons ant implementation --- langton/Main.py | 296 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 langton/Main.py diff --git a/langton/Main.py b/langton/Main.py new file mode 100644 index 0000000..8207b2f --- /dev/null +++ b/langton/Main.py @@ -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()