# Situation
You have been hired by a company making electric garage doors. Accidents with the present product line have resulted in numerous damaged cars, broken limbs and several killed pets. Your mission is to write a safer version of their controller software.

# Specification
We always start with a closed door. The remote control has exactly one button, with the following behaviour.

+ If the door is closed, a push starts opening the door, and vice-versa
+ It takes 5 seconds for the door to open or close completely
+ While the door is moving, one push pauses movement, another push resumes movement in the same direction

In order to make the door safer, it has been equiped with resistance-based obstacle detection. When the door detects an obstacle, it must immediately reverse the direction of movement.

# Input
A string where each character represents one second, with the following possible values.

* ```'.'``` No event
* ```'P'``` Button has been pressed
* ```'O'``` Obstacle has been detected (supersedes P)

As an example, ```'..P....'``` means that nothing happens for two seconds, then the button is pressed, then no further events.

# Output
A string where each character represents one second and indicates the position of the door (0 if fully closed and 5 fully open). The door starts moving immediately, hence its position changes at the same second as the event.

# Example
```..P...O.....``` as input should yield
```001234321000``` as output

Source: codewars | Difficulty: EASY
Tags: Dynamic programming

Fundamentals

Starter code: def controller(events):
	

Solutions:
def controller(events):
	(out, state, dir, moving) = ([], 0, 1, False)
	for c in events:
		if c == 'O':
			dir *= -1
		elif c == 'P':
			moving = not moving
		if moving:
			state += dir
		if state in [0, 5]:
			(moving, dir) = (False, 1 if state == 0 else -1)
		out.append(str(state))
	return ''.join(out)


def controller(events):
	state = 0
	movement = False
	direction = True
	output = ''
	for event in events:
		if event is 'P':
			movement = not movement
		if event is 'O':
			direction = not direction
		state = state + (-1, 1)[direction] * movement
		if state in (0, 5):
			direction = not state
			movement = False
		output += str(state)
	return output


def controller(s):
	(current, pause, door, output, i) = (0, 0, 0, [], 0)
	for symbol in s:
		if symbol == 'P':
			if pause:
				pause = False
			elif not door and (not current):
				door = True
			elif door and current == 5:
				door = False
			else:
				pause = True
		elif symbol == 'O':
			door ^= 1
		current += [[[0, -1][current > 0], [0, 1][current < 5]][door], 0][pause]
		output.append(str(current))
	return ''.join(output)


def controller(events):
	(position, move, resume, positions) = (0, 0, 0, [])
	for event in events:
		if event == 'P':
			if position == 0:
				move = 1
			elif position == 5:
				move = -1
			elif move:
				(resume, move) = (move, 0)
			else:
				(move, resume) = (resume, 0)
		elif event == 'O':
			move = -move
		position = min(5, max(0, position + move))
		if position == 0 or position == 5:
			move = 0
		positions.append(str(position))
	return ''.join(positions)


def controller(events):
	result = []
	(pos, moving, direction) = (0, False, 1)
	for e in events:
		if e == 'P':
			moving = not moving
		elif e == 'O':
			direction *= -1
		if moving:
			pos += direction
			if not 0 < pos < 5:
				direction *= -1
				moving = False
		result.append(pos)
	return ''.join(map(str, result))


class State:

	def __init__(self):
		pass

class PausedIncreasing(State):

	@classmethod
	def handle(cls, e, val):
		nextstate = cls
		if e == '.':
			pass
		elif e == 'P':
			val += 1
			nextstate = MovingIncreasing
		elif e == 'O':
			nextstate = PausedDecreasing
		return (val, nextstate)

class PausedDecreasing(State):

	@classmethod
	def handle(cls, e, val):
		nextstate = cls
		if e == '.':
			pass
		elif e == 'P':
			val -= 1
			nextstate = MovingDecreasing
		elif e == 'O':
			nextstate = PausedIncreasing
		return (val, nextstate)

class MovingIncreasing(State):

	@classmethod
	def handle(cls, e, val):
		nextstate = cls
		if e == '.':
			val += 1
		elif e == 'P':
			nextstate = PausedIncreasing
		elif e == 'O':
			val -= 1
			nextstate = MovingDecreasing
		return (val, nextstate)

class MovingDecreasing(State):

	@classmethod
	def handle(cls, e, val):
		nextstate = cls
		if e == '.':
			val -= 1
		elif e == 'P':
			nextstate = PausedDecreasing
		elif e == 'O':
			val += 1
			nextstate = MovingIncreasing
		return (val, nextstate)

class GarageDoor:

	def __init__(self):
		self.val = 0
		self.state = PausedIncreasing

	def handle(self, e):
		(self.val, self.state) = self.state.handle(e, self.val)
		if self.val == 0:
			self.state = PausedIncreasing
		elif self.val == 5:
			self.state = PausedDecreasing
		return str(self.val)

	def get_output(self, events):
		return ''.join((str(self.handle(e)) for e in events))

def controller(events):
	gd = GarageDoor()
	return gd.get_output(events)


def controller(events: str):
	positions = []
	p = 0
	movement = False
	up = True
	for e in events:
		if e == 'P':
			movement = not movement
		if e == 'O' and movement:
			up = not up
		if movement:
			if up:
				p += 1
				if p == 5:
					up = False
					movement = False
			else:
				p -= 1
				if p == 0:
					up = True
					movement = False
		positions += [p]
	return ''.join((str(p) for p in positions))


def controller(events):
	r = ''
	h = o = p = 0
	for c in events:
		if c == 'P':
			if not o:
				o = 1 if h == 0 else -1
			else:
				p ^= 1
		elif c == 'O':
			o = -o
		if not p:
			h += o
			if h in (0, 5):
				o = 0
		r += f'{h}'
	return r


def controller(events):
	(d, m, opening) = (0, 0, True)
	r = ''
	for e in events:
		if e == 'P':
			if m == 0:
				if d == 0:
					m = 1
					opening = True
				elif d == 5:
					m = -1
					opening = False
				elif opening:
					m = 1
				else:
					m = -1
			else:
				m = 0
		elif e == 'O':
			opening = not opening
			m *= -1
		d += m
		if m == 1 and d == 5 or (m == -1 and d == 0):
			m = 0
		r += str(d)
	return r


def controller(s):
	(h, v, r, m) = (0, 0, '', 1)
	for i in s:
		if i == 'P':
			v = m if v == 0 else -1 if h == 5 else 1 if h == 0 else 0
		if i == 'O':
			v = -1 if v > 0 else 1 if v < 0 else 0
		if v < 0:
			m = -1
		if v > 0:
			m = 1
		h += v
		h = 0 if h < 0 else 5 if h > 5 else h
		r += str(h)
	return r