diff --git a/extras/vba.py b/extras/vba.py index c1ff3538d..b8c3dc9d3 100644 --- a/extras/vba.py +++ b/extras/vba.py @@ -104,6 +104,11 @@ Gb.loadVBA() from vba_config import * +try: + import vba_keyboard as keyboard +except ImportError: + print "Not loading the keyboard module (which uses networkx)." + if not os.path.exists(rom_path): raise Exception("rom_path is not configured properly; edit vba_config.py?") @@ -163,6 +168,10 @@ def button_combiner(buttons): buttons.replace("select", "") result |= button_masks["select"] + if isinstance(buttons, list): + if len(buttons) > 9: + raise Exception("can't combine more than 9 buttons at a time") + for each in buttons: result |= button_masks[each] @@ -826,6 +835,25 @@ class crystal: return output + @staticmethod + def keyboard_apply(button_sequence): + """ + Applies a sequence of buttons to the on-screen keyboard. + """ + for buttons in button_sequence: + press(buttons) + nstep(2) + press([]) + + @staticmethod + def write(something="TrAiNeR"): + """ + Uses a planning algorithm to type out a word in the most efficient way + possible. + """ + button_sequence = keyboard.plan_typing(something) + crystal.keyboard_apply([[x] for x in button_sequence]) + @staticmethod def set_partymon2(): """ @@ -896,6 +924,14 @@ class TestEmulator(unittest.TestCase): self.assertTrue("TRAINER" in text) +class TestWriter(unittest.TestCase): + def test_very_basic(self): + button_sequence = keyboard.plan_typing("an") + expected_result = ["select", "a", "d", "r", "r", "r", "r", "a"] + + self.assertEqual(len(expected_result), len(button_sequence)) + self.assertEqual(expected_result, button_sequence) + if __name__ == "__main__": unittest.main() diff --git a/extras/vba_keyboard.py b/extras/vba_keyboard.py new file mode 100644 index 000000000..bbe85b051 --- /dev/null +++ b/extras/vba_keyboard.py @@ -0,0 +1,563 @@ +# -*- encoding: utf-8 -*- +""" +This file constructs a networkx.DiGraph object called graph, which can be used +to find the shortest path of keypresses on the keyboard to type a word. +""" + +import itertools +import networkx + +graph = networkx.DiGraph() + +graph_data = """ +A a select +A B r +A I l +A lower-upper-column-1 u +A J d + +B b select +B A l +B C r +B lower-upper-column-2 u +B K d + +C c select +C D r +C B l +C lower-upper-column-3 u +C L d + +D d select +D E r +D C l +D del-upper-column-1 u +D M d + +E e select +E del-upper-column-2 u +E N d +E D l +E F r + +F f select +F del-upper-column-3 u +F O d +F E l +F G r + +G g select +G end-upper-column-1 u +G P d +G F l +G H r + +H h select +H end-upper-column-2 u +H Q d +H G l +H I r + +I i select +I end-upper-column-3 u +I R d +I H l +I A r + +J j select +J A u +J S d +J R l +J K r + +K k select +K B u +K T d +K J l +K L r + +L l select +L C u +L U d +L K l +L M r + +M m select +M D u +M V d +M L l +M N r + +N n select +N E u +N W d +N M l +N O r + +O o select +O F u +O X d +O N l +O P r + +P p select +P G u +P Y d +P O l +P Q r + +Q q select +Q H u +Q Z d +Q P l +Q R r + +R r select +R I u +R space-upper-x8-y2 d +R Q l +R J r + +S s select +S J u +S - d +S space-upper-x8-y2 l + +T t select +T K u +T ? d +T S l +T U r + +U u select +U L u +U ! d +U T l +U V r + +V v select +V M u +V / d +V U l +V W r + +W w select +W N u +W . d +W V l +W X r + +X x select +X O u +X , d +X W l +X Y r + +Y y select +Y P u +Y space-upper-x6-y3 d +Y X l +Y Z r + +Z z select +Z Q u +Z space-upper-x7-y3 d +Z Y l +Z space-upper-x8-y2 r + +end-upper-column-1 lower-upper-column-1 r +end-upper-column-2 lower-upper-column-1 r +end-upper-column-3 lower-upper-column-1 r +end-upper-column-1 del-upper-column-1 l +end-upper-column-2 del-upper-column-1 l +end-upper-column-3 del-upper-column-1 l +lower-upper-column-1 end-upper-column-1 l +lower-upper-column-2 end-upper-column-1 l +lower-upper-column-3 end-upper-column-1 l +lower-upper-column-1 del-upper-column-1 r +lower-upper-column-2 del-upper-column-1 r +lower-upper-column-3 del-upper-column-1 r +del-upper-column-1 lower-upper-column-1 l +del-upper-column-2 lower-upper-column-1 l +del-upper-column-3 lower-upper-column-1 l +del-upper-column-1 end-upper-column-1 r +del-upper-column-2 end-upper-column-1 r +del-upper-column-3 end-upper-column-1 r + +lower-upper-column-1 A d +lower-upper-column-2 B d +lower-upper-column-3 C d +lower-upper-column-1 - u +lower-upper-column-2 ? u +lower-upper-column-3 ! u + +del-upper-column-1 D d +del-upper-column-2 E d +del-upper-column-3 F d +del-upper-column-1 / u +del-upper-column-2 . u +del-upper-column-3 , u + +end-upper-column-1 G d +end-upper-column-2 H d +end-upper-column-3 I d +end-upper-column-1 space-upper-x6-y3 u +end-upper-column-2 space-upper-x7-y3 u +end-upper-column-3 space-upper-x8-y3 u + +space-upper-x8-y2 space-lower-x8-y2 select +space-upper-x8-y2 R u +space-upper-x8-y2 space-upper-x8-y3 d +space-upper-x8-y2 Z l +space-upper-x8-y2 S r + +space-upper-x8-y3 MN select +space-upper-x8-y3 space-upper-x8-y2 u +space-upper-x8-y3 end-upper-column-3 d +space-upper-x8-y3 space-upper-x7-y3 l +space-upper-x8-y3 - r + +space-upper-x7-y3 PK select +space-upper-x7-y3 Z u +space-upper-x7-y3 end-upper-column-2 d +space-upper-x7-y3 space-upper-x6-y3 l +space-upper-x7-y3 space-upper-x8-y3 r + +space-upper-x6-y3 ] select +space-upper-x6-y3 Y u +space-upper-x6-y3 end-upper-column-1 d +space-upper-x6-y3 , l +space-upper-x6-y3 space-upper-x7-y3 r + +end-upper-column-1 end-lower-column-1 select +end-upper-column-2 end-lower-column-2 select +end-upper-column-3 end-lower-column-3 select +lower-upper-column-1 lower-lower-column-1 select +lower-upper-column-2 lower-lower-column-2 select +lower-upper-column-3 lower-lower-column-3 select +del-upper-column-1 del-lower-column-1 select +del-upper-column-2 del-lower-column-2 select +del-upper-column-3 del-lower-column-3 select + +lower-lower-column-1 × u +lower-lower-column-2 ( u +lower-lower-column-3 ) u +lower-lower-column-1 a d +lower-lower-column-2 b d +lower-lower-column-3 c d + +end-lower-column-1 ] u +end-lower-column-2 PK u +end-lower-column-3 MN u +end-lower-column-1 g d +end-lower-column-2 h d +end-lower-column-3 i d + +del-lower-column-1 : u +del-lower-column-2 ; u +del-lower-column-3 [ u +del-lower-column-1 d d +del-lower-column-2 e d +del-lower-column-3 f d + +- × select +- S u +- lower-upper-column-1 d +- space-upper-x8-y3 l +- ? r + +? ( select +? T u +? lower-upper-column-2 d +? - l +? ! r + +! ) select +! U u +! lower-upper-column-3 d +! ? l +! / r + +/ : select +/ V u +/ del-upper-column-1 d +/ ! l +/ . r + +. ; select +. W u +. del-upper-column-2 d +. / l +. , r + +, [ select +, X u +, del-upper-column-3 d +, . l +, space-upper-x6-y3 r + +× - select +× s u +× upper-lower-column-1 d +× MN l +× ( r + +( ? select +( t u +( upper-lower-column-2 d +( × l +( ) r + +) ! select +) u u +) upper-lower-column-3 d +) ( l +) : r + +: / select +: v u +: del-lower-column-1 d +: ) l +: ; r + +; . select +; w u +; del-lower-column-2 d +; : l +; [ r + +[ , select +[ x u +[ del-lower-column-3 d +[ ; l +[ ] r + +] space-upper-x6-y3 select +] y u +] end-lower-column-1 d +] [ l +] PK r + +PK space-upper-x7-y3 select +PK z u +PK end-lower-column-2 d +PK ] l +PK MN r + +MN space-upper-x8-y3 select +MN space-lower-x8-y2 u +MN end-lower-column-3 d +MN PK l +MN × r + +space-lower-x8-y2 space-upper-x8-y2 select +space-lower-x8-y2 r u +space-lower-x8-y2 MN d +space-lower-x8-y2 z l +space-lower-x8-y2 s r + +a A select +a upper-lower-column-1 u +a j d +a i l +a b r + +b B select +b upper-lower-column-2 u +b k d +b a l +b c r + +c C select +c upper-lower-column-3 u +c l d +c b l +c d r + +d D select +d del-lower-column-1 u +d m d +d c l +d e r + +e E select +e del-lower-column-2 u +e n d +e d l +e f r + +f F select +f del-lower-column-3 u +f o d +f e l +f g r + +g G select +g end-lower-column-1 u +g p d +g f l +g h r + +h H select +h end-lower-column-2 u +h q d +h g l +h i r + +i I select +i end-lower-column-3 u +i r d +i h l +i a r + +j J select +j a u +j s d +j r l +j k r + +k K select +k b u +k t d +k j l +k l r + +l L select +l c u +l u d +l k l +l m r + +m M select +m d u +m v d +m l l +m n r + +n N select +n e u +n w d +n m l +n o r + +o O select +o f u +o x d +o n l +o p r + +p P select +p g u +p y d +p o l +p q r + +q Q select +q h u +q z d +q p l +q r r + +r R select +r i u +r space-lower-x8-y2 d +r q l +r j r + +s S select +s j u +s × d +s space-lower-x8-y2 l +s t r + +t T select +t k u +t ( d +t s l +t u r + +u U select +u l u +u ) d +u t l +u v r + +v V select +v m u +v : d +v u l +v w r + +w W select +w n u +w ; d +w v l +w x r + +x X select +x o u +x [ d +x w l +x y r + +y Y select +y p u +y ] d +y x l +y z r + +z Z select +z q u +z PK d +z y l +z space-lower-x8-y2 r""" + +for line in graph_data.split("\n"): + if line == "": + continue + elif line[0] == "#": + continue + + (node1, node2, edge_name) = line.split(" ") + graph.add_edge(node1, node2, key=edge_name) + + #print "Adding edge ("+edge_name+") "+node1+" -> "+node2 + +def shortest_path(node1, node2): + """ + Figures out the shortest list of button presses to move from one letter to + another. + """ + buttons = [] + last = None + path = networkx.shortest_path(graph, node1, node2) + for each in path: + if last != None: + buttons.append(convert_nodes_to_button_press(last, each)) + last = each + return buttons + #return [convert_nodes_to_button_press(node3, node4) for (node3, node4) in zip(*(iter(networkx.shortest_path(graph, node1, node2)),) * 2)] + +def convert_nodes_to_button_press(node1, node2): + """ + Determines the button necessary to switch from node1 to node2. + """ + print "getting button press for state transition: " + node1 + " -> " + node2 + return graph.get_edge_data(node1, node2)["key"] + +def plan_typing(text, current="A"): + """ + Plans a sequence of button presses to spell out the given text. + """ + buttons = [] + for target in text: + if target == current: + buttons.append("a") + else: + print "Finding the shortest path between " + current + " and " + target + more_buttons = shortest_path(current, target) + buttons.extend(more_buttons) + buttons.append("a") + current = target + return buttons +