# Background


My TV remote control has arrow buttons and an `OK` button.

I can use these to move a "cursor" on a logical screen keyboard to type words...

# Keyboard

The screen "keyboard" layout looks like this


  #tvkb {
    width : 400px;
    border: 5px solid gray; border-collapse: collapse;
  }
  #tvkb td {
    color : orange;
    background-color : black;
    text-align : center;
    border: 3px solid gray; border-collapse: collapse;
  }


abcde123
fghij456
klmno789
pqrst.@0
uvwxyz_/
aASP



* `aA` is the SHIFT key. Pressing this key toggles alpha characters between UPPERCASE and lowercase
* `SP` is the space character
* The other blank keys in the bottom row have no function

# Kata task

How many button presses on my remote are required to type the given `words`?

## Hint

This Kata is an extension of the earlier ones in this series. You should complete those first.

## Notes

* The cursor always starts on the letter `a` (top left)
* The alpha characters are initially lowercase (as shown above)
* Remember to also press `OK` to "accept" each letter
* Take the shortest route from one letter to the next
* The cursor wraps, so as it moves off one edge it will reappear on the opposite edge
* Although the blank keys have no function, you may navigate through them if you want to
* Spaces may occur anywhere in the `words` string
* Do not press the SHIFT key until you need to. For example, with the word `e.Z`, the SHIFT change happens **after** the `.` is pressed (not before)
 
# Example

words = `Code Wars`

* C => `a`-`aA`-OK-`A`-`B`-`C`-OK = 6
* o => `C`-`B`-`A`-`aA`-OK-`u`-`v`-`w`-`x`-`y`-`t`-`o`-OK = 12
* d => `o`-`j`-`e`-`d`-OK = 4
* e => `d`-`e`-OK = 2
* space => `e`-`d`-`c`-`b`-`SP`-OK = 5
* W => `SP`-`aA`-OK-`SP`-`V`-`W`-OK = 6
* a => `W`-`V`-`U`-`aA`-OK-`a`-OK = 6
* r => `a`-`f`-`k`-`p`-`q`-`r`-OK = 6
* s => `r`-`s`-OK = 2

Answer = 6 + 12 + 4 + 2 + 5 + 6 + 6 + 6 + 2 = 49



*Good Luck!
DM.*



Series
* TV Remote
* TV Remote (shift and space)
* TV Remote (wrap)
* TV Remote (symbols)

Difficulty: EASY
Starter code: def tv_remote(words):
	

Solutions:
import re
(H, W) = (6, 8)
KEYBOARD = 'abcde123fghij456klmno789pqrst.@0uvwxyz_/* '
MAP = {c: (i // W, i % W) for (i, c) in enumerate(KEYBOARD)}

def manhattan(*pts):
	dxy = [abs(z2 - z1) for (z1, z2) in zip(*pts)]
	return 1 + sum((min(dz, Z - dz) for (dz, Z) in zip(dxy, (H, W))))

def toggle(m):
	(ups, end) = m.groups()
	return f'*{ups.lower()}*{end}'

def tv_remote(words):
	reWords = re.sub('([A-Z][^a-z]*)([a-z]?)', toggle, words).rstrip('*')
	return sum((manhattan(MAP[was], MAP[curr]) for (was, curr) in zip('a' + reWords, reWords)))


from collections import namedtuple
Position = namedtuple('Position', 'y x')

def tv_remote(word: str):
	remote = ('a', 'b', 'c', 'd', 'e', '1', '2', '3', 'f', 'g', 'h', 'i', 'j', '4', '5', '6', 'k', 'l', 'm', 'n', 'o', '7', '8', '9', 'p', 'q', 'r', 's', 't', '.', '@', '0', 'u', 'v', 'w', 'x', 'y', 'z', '_', '/', '', ' ')
	(shift, shift_pressed) = (Position(5, 0), False)
	prev = Position(0, 0)
	button_presses = 0
	for letter in word:
		if letter.isalpha() and letter.isupper() != shift_pressed:
			button_presses += calc_presses(prev, shift)
			(prev, shift_pressed) = (shift, not shift_pressed)
		cur = Position(*divmod(remote.index(letter.lower()), 8))
		button_presses += calc_presses(prev, cur)
		prev = cur
	return button_presses

def calc_presses(pos1: Position, pos2: Position):
	(dif_y, dif_x) = (abs(pos1.y - pos2.y), abs(pos1.x - pos2.x))
	return min(dif_y, 6 - dif_y) + min(dif_x, 8 - dif_x) + 1


def tv_remote(word):
	(caps, cases, shift) = (coords['↑'], (str.isupper, str.islower), 0)
	(moves, current) = (0, (0, 0))
	for char in word:
		target = coords[char.lower()]
		if cases[shift](char):
			(moves, current, shift) = (moves + distance(current, caps), caps, 1 - shift)
		(moves, current) = (moves + distance(current, target), target)
	return moves
keyboard = ('abcde123', 'fghij456', 'klmno789', 'pqrst.@0', 'uvwxyz_/', '↑ ')
coords = {char: (line.index(char), y) for (y, line) in enumerate(keyboard) for char in line}

def distance(pos1, pos2):
	(d0, d1) = (abs(pos2[0] - pos1[0]), abs(pos2[1] - pos1[1]))
	return 1 + min(d0, 8 - d0) + min(d1, 6 - d1)


def navigate(s, e):
	a = 'abcde123fghij456klmno789pqrst.@0uvwxyz_/*&'
	d = {i: [a.index(i) // 8, a.index(i) % 8] for i in a}
	x = 0
	y = 0
	i = 0
	j = 0
	rowdis = 0
	coldis = 0
	for (k, v) in d.items():
		if k == s:
			i = v[0]
			j = v[1]
		if k == e:
			x = v[0]
			y = v[1]
		if abs(i - x) > 2.5:
			if i < x:
				rowdis = 6 + i - x
			else:
				rowdis = 6 - i + x
		else:
			rowdis = abs(i - x)
		if abs(j - y) > 3.5:
			if j < y:
				coldis = 8 + j - y
			else:
				coldis = 8 - j + y
		else:
			coldis = abs(j - y)
	return rowdis + coldis

def tv_remote(word):
	ans = 0
	cursor = 'a'
	shift = False
	for c in word:
		if c.isspace():
			ans += navigate(cursor, '&') + 1
			cursor = '&'
			continue
		if c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
			if c.isupper():
				if not shift:
					ans += navigate(cursor, '*') + 1
					shift = True
					cursor = '*'
			if c.islower():
				if shift:
					ans += navigate(cursor, '*') + 1
					shift = False
					cursor = '*'
		ans += navigate(cursor, c.lower()) + 1
		cursor = c.lower()
	return ans


def tv_remote(word):
	matrix = [['a', 'b', 'c', 'd', 'e', '1', '2', '3'], ['f', 'g', 'h', 'i', 'j', '4', '5', '6'], ['k', 'l', 'm', 'n', 'o', '7', '8', '9'], ['p', 'q', 'r', 's', 't', '.', '@', '0'], ['u', 'v', 'w', 'x', 'y', 'z', '_', '/'], ['aA', ' ']]
	vertical_inversion = {0: 0, 1: 1, 2: 2, 3: 3, 4: 2, 5: 1}
	horizontal_inversion = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 3, 6: 2, 7: 1}
	actions = ([0, 0],)
	upper_mode = False
	press_ok = 0
	func = lambda x, y: vertical_inversion[abs(x[0] - y[0])] + horizontal_inversion[abs(x[1] - y[1])]
	for char in word:
		for i in range(6):
			if char.lower() in matrix[i]:
				if char.isupper() and (not upper_mode):
					actions += ([5, 0],)
					upper_mode = True
					press_ok += 1
					actions += ([i, matrix[i].index(char.lower())],)
					press_ok += 1
				elif char.isupper() and upper_mode:
					actions += ([i, matrix[i].index(char.lower())],)
					press_ok += 1
				elif char.islower() and upper_mode:
					actions += ([5, 0],)
					upper_mode = False
					press_ok += 1
					actions += ([i, matrix[i].index(char.lower())],)
					press_ok += 1
				elif char.islower() and (not upper_mode):
					actions += ([i, matrix[i].index(char.lower())],)
					press_ok += 1
				else:
					actions += ([i, matrix[i].index(char.lower())],)
					press_ok += 1
	return sum([func(i[0], i[1]) for i in list(zip(actions, actions[1:]))]) + press_ok


D = {v: (r, c) for (r, row) in enumerate(['abcde123', 'fghij456', 'klmno789', 'pqrst.@0', 'uvwxyz_/', 'U ??????']) for (c, v) in enumerate(row)}
mind = lambda d, mx: min(d, mx - d)
move = lambda t, lastr, lastc, r, c: (t + mind(abs(r - lastr), 6) + mind(abs(c - lastc), 8) + 1, r, c)

def tv_remote(words):
	(t, lastr, lastc, lower) = (0, 0, 0, True)
	for e in words:
		if e.isalpha() and lower != e.islower():
			(t, lastr, lastc) = move(t, lastr, lastc, 5, 0)
			lower = not lower
		(t, lastr, lastc) = move(t, lastr, lastc, *D[e.lower()])
	return t


SHIFT = 'aA'
syms = {x: (i, j) for (i, row) in enumerate(['abcde123', 'fghij456', 'klmno789', 'pqrst.@0', 'uvwxyz_/']) for (j, x) in enumerate(row)}
syms[' '] = (5, 1)
syms[SHIFT] = (5, 0)

def tv_remote(words):
	r = c = 0
	n = 0
	lower = True

	def press(x):
		nonlocal n, r, c, lower
		(r1, c1) = syms[x]
		n += min(abs(r1 - r), 6 - abs(r1 - r)) + min(abs(c1 - c), 8 - abs(c1 - c)) + 1
		(r, c) = (r1, c1)
		if x == SHIFT:
			lower = not lower
	for ch in words:
		if ch.isalpha() and ch.islower() != lower:
			press(SHIFT)
		press(ch.lower())
	return n


import re

def tv_remote(words):
	letters = {c: (x, y) for (y, row) in enumerate(('abcde123', 'fghij456', 'klmno789', 'pqrst.@0', 'uvwxyz_/', '⇧ ')) for (x, c) in enumerate(row)}
	words = re.sub('((?:^|[a-z])[^A-Z]*)([A-Z])', '\\1⇧\\2', words)
	words = re.sub('([A-Z][^a-z]*)([a-z])', '\\1⇧\\2', words)
	words = words.lower()
	return sum((min(abs(letters[c1][0] - letters[c2][0]), 8 - abs(letters[c1][0] - letters[c2][0])) + min(abs(letters[c1][1] - letters[c2][1]), 6 - abs(letters[c1][1] - letters[c2][1])) + 1 for (c1, c2) in zip('a' + words, words)))


D = {v: [r, c] for (r, row) in enumerate(['abcde123', 'fghij456', 'klmno789', 'pqrst.@0', 'uvwxyz_/', '* ']) for (c, v) in enumerate(row)}

def manhattan_dist(*points):
	print('points md: ', points)
	return [abs(z2 - z1) for (z1, z2) in zip(*points)]

def wrap_manhattan_dist(*points):
	dist = manhattan_dist(*points)
	wdist = [min(dist[0], 6 - dist[0]), min(dist[1], 8 - dist[1])]
	return sum(wdist)
import re

def tv_remote(words):
	words = re.sub('([A-Z])', '*\\1*', words).lower()
	words = re.sub('\\*([\\d.@_/ ]*)(\\*|$)', '\\1', words)
	words = re.sub('\\*$', '', words)
	words = re.sub('\\*([0-9.@_/ ]+)([^\\*])', '\\1*\\2', words)
	words = 'a' + words
	moves = sum([wrap_manhattan_dist(D[words[i]], D[words[i + 1]]) + 1 for i in range(len(words) - 1)])
	return moves


def tv_remote(words: str):
	screen_keyboard_symbols = 'abcde123fghij456klmno789pqrst.@0uvwxyz_/* '
	symbols_map = {c: (i // 8, i % 8) for (i, c) in enumerate(screen_keyboard_symbols)}
	kb_dims = (len(screen_keyboard_symbols) // 8 + 1, 8)

	def dist(pos1, pos2):
		return sum((min(abs(p1 - p2), dim - abs(p1 - p2)) for (p1, p2, dim) in zip(pos1, pos2, kb_dims)))
	button_presses = 0
	(cur_position, next_position) = ((0, 0), (0, 0))
	caps_on = False
	words_to_type = []
	for ch in words:
		if ch.islower() and caps_on or (ch.isupper() and (not caps_on)):
			words_to_type.append('*' + ch.lower())
			caps_on = not caps_on
		else:
			words_to_type.append(ch.lower())
	words_to_type = ''.join(words_to_type)
	for ch in words_to_type:
		if ch in screen_keyboard_symbols:
			next_position = symbols_map[ch]
			button_presses += 1 + dist(cur_position, next_position)
			cur_position = next_position
		else:
			return -1
	return button_presses