---

# Hint


This Kata is an extension of the earlier ones in this series. Completing those first will make this task easier.

# 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" layouts look 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;
  }
  #legend {
    width : 400px;
    border: 1px solid gray; border-collapse: collapse;
  }
  #legend td {
    text-align : center;
    border: 1px solid gray; border-collapse: collapse;
  }



Keypad Mode 1 = alpha-numeric (lowercase)
Keypad Mode 3 = symbols




abcde123
fghij456
klmno789
pqrst.@0
uvwxyz_/
aA#SP




^~?!'"()
-:;+&%*=
<>€£$¥¤\
[]{},.@§
#¿¡_/
aA#SP






* `aA#` is the SHIFT key. Pressing this key cycles through THREE keypad modes.
 * **Mode 1** = alpha-numeric keypad with lowercase alpha (as depicted above)
 * **Mode 2** = alpha-numeric keypad with UPPERCASE alpha
 * **Mode 3** = symbolic keypad (as depicted above)
 
 
* `SP` is the space character
* The other (solid fill) keys in the bottom row have no function

## Special Symbols

For your convenience, here are Unicode values for the less obvious symbols of the **Mode 3** keypad


¡ = U-00A1£ = U-00A3¤ = U-00A4¥ = U-00A5
§ = U-00A7¿ = U-00BF€ = U-20AC


# Kata task

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

## Notes

* The cursor always starts on the letter `a` (top left)
* The inital keypad layout is **Mode 1**
* 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). In other words, do not try to optimize total key presses by pressing SHIFT early.

```if:c,cpp
## C/C++ warriors
The standard I/O libraries choke on wide characters beyond the value 255. This kata includes the Euro € (U-20AC). So the function `ws2utf8()` has been preloaded for converting wchar_t strings to UTF-8 for printing.
```

# Example

words = `Too Easy?`

* T => `a`-`aA#`-OK-`U`-`V`-`W`-`X`-`Y`-`T`-OK = 9
* o => `T`-`Y`-`X`-`W`-`V`-`U`-`aA#`-OK-OK-`a`-`b`-`c`-`d`-`e`-`j`-`o`-OK = 16
* o => `o`-OK = 1
* space => `o`-`n`-`m`-`l`-`q`-`v`-`SP`-OK = 7
* E => `SP`-`aA#`-OK-`A`-`3`-`2`-`1`-`-E`-OK = 8
* a => `E`-`1`-`2`-`3`-`A`-`aA`-OK-OK-`a`-OK = 9
* s => `a`-`b`-`c`-`d`-`i`-`n`-`s`-OK = 7
* y => `s`-`x`-`y`-OK = 3
* ? => `y`-`x`-`w`-`v`-`u`-`aA#`-OK-OK-`^`-`~`-`?`-OK = 11

Answer = 9 + 16 + 1 + 7 + 8 + 9 + 7 + 3 + 11 = 71



*Good Luck!
DM.*



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

```python

USER_FILENAME = "<user_code>"
import sys, json, builtins
import math, heapq, bisect, itertools, functools, collections, statistics, fractions, decimal, operator, array, random, re, string, os, pathlib
try:
    import resource as _res
except Exception:
    _res = None
from collections import Counter, defaultdict, deque, OrderedDict
from functools import lru_cache, reduce
from itertools import accumulate, permutations, combinations, product
from math import gcd, ceil, floor, sqrt
from decimal import Decimal
try:
    from typing import *
except Exception:
    pass
try:
    sys.setrecursionlimit(1_000_000)
except Exception:
    pass
try:
    import site as _site
    try:
        _sys_sites = _site.getsitepackages() if hasattr(_site, "getsitepackages") else []
        for _p in _sys_sites:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
    try:
        _usp = _site.getusersitepackages()
        if isinstance(_usp, str):
            _usp = [_usp]
        for _p in _usp:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
except Exception:
    pass

def _disabled_input(*args, **kwargs):
    raise RuntimeError("input() is disabled in callable mode")
builtins.input = _disabled_input

ns = {"__name__": "__judge__"}  # prevent if __name__ == "__main__" blocks
for _k, _v in list(globals().items()):
    if not _k.startswith("_"):
        ns[_k] = _v
_CODE = 'def tv_remote(words):\n    page1 = {\'a\':[0,0],\'b\':[0,1],\'c\':[0,2],\'d\':[0,3],\'e\':[0,4],\'1\':[0,5],\'2\':[0,6],\'3\':[0,7],\n             \'f\':[1,0],\'g\':[1,1],\'h\':[1,2],\'i\':[1,3],\'j\':[1,4],\'4\':[1,5],\'5\':[1,6],\'6\':[1,7],\n             \'k\':[2,0],\'l\':[2,1],\'m\':[2,2],\'n\':[2,3],\'o\':[2,4],\'7\':[2,5],\'8\':[2,6],\'9\':[2,7],\n             \'p\':[3,0],\'q\':[3,1],\'r\':[3,2],\'s\':[3,3],\'t\':[3,4],\'.\':[3,5],\'@\':[3,6],\'0\':[3,7],\n             \'u\':[4,0],\'v\':[4,1],\'w\':[4,2],\'x\':[4,3],\'y\':[4,4],\'z\':[4,5],\'_\':[4,6],\'/\':[4,7],\n             \'aA#\':[5,0],\' \':[5,1]}\n    page2 = {\'A\':[0,0],\'B\':[0,1],\'C\':[0,2],\'D\':[0,3],\'E\':[0,4],\'1\':[0,5],\'2\':[0,6],\'3\':[0,7],\n             \'F\':[1,0],\'G\':[1,1],\'H\':[1,2],\'I\':[1,3],\'J\':[1,4],\'4\':[1,5],\'5\':[1,6],\'6\':[1,7],\n             \'K\':[2,0],\'L\':[2,1],\'M\':[2,2],\'N\':[2,3],\'O\':[2,4],\'7\':[2,5],\'8\':[2,6],\'9\':[2,7],\n             \'P\':[3,0],\'Q\':[3,1],\'R\':[3,2],\'S\':[3,3],\'T\':[3,4],\'.\':[3,5],\'@\':[3,6],\'0\':[3,7],\n             \'U\':[4,0],\'V\':[4,1],\'W\':[4,2],\'X\':[4,3],\'Y\':[4,4],\'Z\':[4,5],\'_\':[4,6],\'/\':[4,7],\n             \'aA#\':[5,0],\' \':[5,1]}\n    page3 = {\'^\':[0,0],\'~\':[0,1],\'?\':[0,2],\'!\':[0,3],\'\\\'\':[0,4],\'"\':[0,5],\'(\':[0,6],\')\':[0,7],\n             \'-\':[1,0],\':\':[1,1],\';\':[1,2],\'+\':[1,3],\'&\':[1,4],\'%\':[1,5],\'*\':[1,6],\'=\':[1,7],\n             \'<\':[2,0],\'>\':[2,1],\'€\':[2,2],\'£\':[2,3],\'$\':[2,4],\'¥\':[2,5],\'¤\':[2,6],\'\\\\\':[2,7],\n             \'[\':[3,0],\']\':[3,1],\'{\':[3,2],\'}\':[3,3],\',\':[3,4],\'.\':[3,5],\'@\':[3,6],\'§\':[3,7],\n             \'#\':[4,0],\'¿\':[4,1],\'¡\':[4,2],\'_\':[4,6],\'/\':[4,7],\n             \'aA#\':[5,0],\' \':[5,1]}\n\n    pages = {1:page1,2:page2,3:page3}\n\n    def move_to_page(position,current_page,target_page):\n        moves = shortest_distance(position,[5,0])\n        if current_page == 3:\n            if target_page == 1:\n                moves += 1\n            elif target_page == 2:\n                moves += 2\n        elif current_page == 2:\n            if target_page == 1:\n                moves += 2\n            elif target_page == 3:\n                moves += 1\n        else:\n            moves += target_page - current_page\n        return moves\n\n    def find_page(character):\n        for page in pages:\n            if character in pages[page]:\n                return int(page)\n\n    def shortest_distance(pos1,pos2):\n        moves = 0\n        delta_y = abs(pos2[0]-pos1[0])\n        delta_x = abs(pos2[1]-pos1[1])\n        return min(delta_x,8-delta_x)+min(delta_y,6-delta_y)\n\n    debug = False\n    sequence = list(words)\n    moves = 0\n    position = [0,0]\n    current_page = 1\n\n    for element in sequence:\n        if element not in pages[current_page]:\n            target_page = find_page(element)\n            moves += move_to_page(position,current_page,target_page)\n            position = [5,0]\n            current_page = target_page\n        target_position = pages[current_page][element]\n        moves += shortest_distance(position,target_position)\n        position = target_position\n        moves += 1\n    return moves'
_co = compile(_CODE, USER_FILENAME, "exec")
exec(_co, ns, ns)

_target = None
_fn_name = 'tv_remote'
if _fn_name:
    if "Solution" in ns:
        try:
            _obj = ns["Solution"]()
            if hasattr(_obj, _fn_name):
                _target = getattr(_obj, _fn_name)
        except Exception:
            _target = None
    if _target is None and _fn_name in ns and callable(ns[_fn_name]):
        _target = ns[_fn_name]
# Fallback: if there's exactly one user-defined function, use it
if _target is None:
    _cands = [v for k, v in ns.items() if callable(v) and getattr(v, "__module__", "") in ("__judge__", "__main__")]
    _user_funcs = [c for c in _cands if getattr(c, "__name__", "").startswith("_") is False]
    if len(_user_funcs) == 1:
        _target = _user_funcs[0]
if _target is None:
    raise RuntimeError(f"Could not resolve callable '{_fn_name}'")

_raw = sys.stdin.read().strip()
if _raw:
    _parsed = json.loads(_raw)
    if isinstance(_parsed, dict):
        _kwargs = _parsed.get("kwargs", {})
        _args = _parsed.get("args", [])
    else:
        _args, _kwargs = _parsed, {}
else:
    _args, _kwargs = [], {}
if not isinstance(_args, (list, tuple)):
    _args = [_args]
_res = _target(*_args, **_kwargs)
try:
    sys.stdout.write(repr(_res) + "\n")
except Exception:
    pass

```

Measured memory_bytes: 19834