|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- 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)
|