#
# ca2d.py: 2D cellular automaton simulation engine
#          BML Model of bike traffic
#          Required by bike.py
#
"""2D cellular automaton simulation engine for BML Model of bike traffic"""

from numpy import zeros
from random import randint

class ca2d:
	"""Two-Dimensional Cellular Automata class"""
	def __init__ (self,size,vbikes,hbikes):
		self.size = size
		self.vbikes = vbikes
		self.hbikes = hbikes
		self.curr = zeros((size,size))
		self.nxt = zeros((size,size))
		
		# put down initial east-west bikes
		for i in xrange(hbikes):
			row = randint(0, size-1)	
			col = randint(0, size-1)			

			while self.curr[row][col] != 0:
				row = randint(0, size-1)	# pick random location
				col = randint(0, size-1)
			self.curr[row][col] = 1
		
		# put down initial north-south bikes
		for j in xrange(vbikes):
			row = randint(0,size-1)
			col = randint(0,size-1)			

			while self.curr[row][col] != 0:
				row = randint(0, size-1)	# pick random location
				col = randint(0, size-1)
			self.curr[row][col] = 2
		
		self.nxt[:][:] = self.curr[:][:]	# copy current state to next state
		
	# prints out current state of CA
	def disp(self):
		"""Print out the current state of the CA."""
		for i in xrange(self.size):
			print self.curr[i][:]

	def steph(self):
		"""Incremental step for horizontal-moving vehicles"""
		for i in xrange(self.size):
			for j in xrange(self.size):
				# Horizontal movement
				if self.curr[i][j] == 1:
				
					# Horizontal boundary condition
					# check if not at right-most position
					if j != self.size - 1:
						
						# check if position to right is open
						if self.curr[i][j+1] == 0:
							self.nxt[i][j+1] = 1
							self.nxt[i][j] = 0
						# else keep same position... do nothing
					# at right-most position, check if left-most is open
					elif self.curr[i][0] == 0:
						self.nxt[i][0] = 1
						self.nxt[i][j] = 0
		#current state becomes next state
		self.curr[:][:] = self.nxt[:][:]

	def stepv(self):
		"""Incremental step for vertical-moving vehicles"""
		for i in xrange(self.size):
			for j in xrange(self.size):
				# Vertical movement
				if self.curr[i][j] == 2:
					# Vertical boundary condition
					# check if not at bottom-most position
					if i != self.size - 1:
					
						# check if position below is open
						if self.curr[i+1][j] == 0:
							self.nxt[i+1][j] = 2
							self.nxt[i][j] = 0
						# else keep same position... do nothing
					# at bottom-most position, check if top-most is open							
					elif self.curr[0][j] == 0:
						self.nxt[0][j] = 2
						self.nxt[i][j] = 0
		#current state becomes next state
		self.curr[:][:] = self.nxt[:][:]
