Project 15 — Markov Chain Chatbot · Project 16 — TUI App with curses / textual
Build a text generator chatbot using Markov chains. Train it on any text corpus and generate statistically plausible responses. Build a terminal user interface (TUI) for one of your earlier projects. Use curses for low-level control or the modern textual framework for high-level widgets.
What you'll learn
- 1Build an n-gram frequency model from text
- 2Generate text by sampling from the model
- 3Handle sentence boundaries naturally
- 4Train on multiple corpora and blend responses
- 5Set up a curses or textual application skeleton
- 6Render windows, panels, and text widgets
- 7Handle keyboard input and navigation
- 8Port an existing CLI app to TUI
Project 15 — Markov Chain Chatbot
Build a text generator chatbot using Markov chains. Train it on any text corpus and generate statistically plausible responses.
Algorithm
A Markov chain model predicts the next word based only on the previous N words (N-gram). For N=2: given (word1, word2), sample the most likely word3 from training data.
from collections import defaultdict
import random
class MarkovChain:
def __init__(self, n=2):
self.n = n
self.model = defaultdict(list)
def train(self, text: str):
words = text.split()
for i in range(len(words) - self.n):
key = tuple(words[i:i+self.n])
self.model[key].append(words[i+self.n])
def generate(self, length=50, seed=None):
if seed is None:
seed = random.choice(list(self.model.keys()))
output = list(seed)
for _ in range(length - self.n):
key = tuple(output[-self.n:])
nexts = self.model.get(key)
if not nexts: break
output.append(random.choice(nexts))
return ' '.join(output)Project 16 — TUI App with curses / textual
Build a terminal user interface (TUI) for one of your earlier projects. Use curses for low-level control or the modern textual framework for high-level widgets.
Option A: curses (stdlib)
import curses
def main(stdscr):
curses.curs_set(0)
stdscr.clear()
h, w = stdscr.getmaxyx()
stdscr.addstr(h//2, w//2-10, "Hello from curses!")
stdscr.addstr(h-1, 0, "Press q to quit")
stdscr.refresh()
while True:
key = stdscr.getch()
if key == ord("q"):
break
curses.wrapper(main)Option B: textual (third-party)
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Button, Label
class MyApp(App):
def compose(self) -> ComposeResult:
yield Header()
yield Label("Hello, Textual!")
yield Button("Click me", id="btn")
yield Footer()
def on_button_pressed(self, event: Button.Pressed) -> None:
self.query_one(Label).update("Button was pressed!")
if __name__ == "__main__":
MyApp().run()Suggested project: TUI To-Do
- Main pane: task list with arrow-key navigation
- Status bar: task count and current filter
- Keybindings: a=add, d=delete, space=toggle done, q=quit, /=search
- Input dialog: popup for task title
📝 Exercises
Try them yourself first, then open the solution to compare.
Build the Markov chatbot
Goal: Train a Markov model on a text file and create a chatbot loop.
- Build bigram model from corpus
- Generate 50-word responses
- Interactive chat loop
- Load corpus from file
Build a TUI app
Goal: Port your to-do app (Project 5) to a TUI using curses or textual.
- Task list with keyboard navigation
- Add/delete/toggle done via keybindings
- Status bar with count
- Search/filter pane
- · Renders correctly in terminal — 30%
- · Keyboard navigation — 30%
- · CRUD operations — 30%
- · No crashes on resize — 10%
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗Comments 0
No comments yet. Be the first.
Only members can comment.
Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy