commit f581515ada31708486ab414c4e3dedb59ff4c300 Author: Pete Shadbolt Date: Mon Mar 15 20:06:24 2021 -0700 Working diff --git a/cheekyfsm.py b/cheekyfsm.py new file mode 100644 index 0000000..f7d26fc --- /dev/null +++ b/cheekyfsm.py @@ -0,0 +1,117 @@ +import curses +import random +import sys +import re + +ESCAPE = 27 +LETTERS = range(97, 122) +NUMBERS = range(48, 57) + + +class CheekyFSM(object): + def __init__(self): + self.lastkey = -1 + self.buffer = "" + self.message = "" + self.edges = {} + self.state = "A" + self.history = " "*30 + + def show_edges(self): + """ Print the help menu """ + y, x = self.w.getmaxyx() + for index, ((start, end), prob) in enumerate(self.edges.items()): + self.w.addstr(10 + index, 2, f"{start} -> {end} : {prob}%") + + def update(self): + """ Update the FSM """ + edges = list(self.edges.items()) + random.shuffle(edges) + for ((start, end), prob) in edges: + if start == self.state: + if random.randint(0, 100) < prob: + self.state = end + + self.history = self.history[1:] + self.state + + def show_state(self): + """ Show the state of the machine """ + self.w.addstr(2, 2, f"Tempo: 120bpm") + self.w.addstr(4, 2, f"State: {self.state}") + self.w.addstr(5, 2, f"History: {self.history}") + + offset = 6 + for ((start, end), prob) in self.edges.items(): + if start == self.state: + self.w.addstr(offset, 2, + f"Going to {end} with probability {prob}%") + offset += 1 + + def show_status(self): + """ Print the status bar """ + y, x = self.w.getmaxyx() + self.w.addstr(y - 2, 2, f"> {self.buffer}", curses.A_BOLD) + + def quit(self): + """ Quit the program """ + curses.nocbreak() + self.w.keypad(False) + curses.echo() + curses.endwin() + sys.exit(0) + + def process_key(self): + """ Process a key """ + key = self.w.getch() + self.lastkey = key + if key == ESCAPE: + quit(self.w) + elif key in LETTERS: + self.buffer += chr(key).upper() + elif key in NUMBERS: + self.buffer += chr(key) + elif key == 10: + self.accept() + elif key == 263 and len(self.buffer) > 0: + self.buffer = self.buffer[:-1] + + def accept(self): + """ Accept the string """ + match = re.match(r"([A-Z])([A-Z])(\d+)", self.buffer) + if not match: + self.message = " Invalid command :(" + else: + groups = match.groups() + start = groups[0] + end = groups[1] + prob = int(groups[2]) + self.modify(start, end, prob) + self.buffer = "" + + def modify(self, start, end, prob): + """ Make a modification """ + key = (start, end) + self.edges[key] = prob + self.message = f" {start} -> {end} : {prob}%" + if prob == 0 and key in self.edges: + self.message = f" {start} -> {end} : deleted" + del self.edges[key] + + def loop(self, w): + """ Run the loop """ + self.w = w + curses.curs_set(0) # invisible + w.timeout(100) + while True: + w.clear() + self.show_edges() + self.show_status() + self.show_state() + self.update() + w.refresh() + self.process_key() + + +if __name__ == "__main__": + fsm = CheekyFSM() + curses.wrapper(fsm.loop)