Society of Books

2024-07-13

A few years ago I created a Bookclub site (you can see the public-facing side of it here. Use the username 'guest' and no password), initially to serve my book club of 20 years and 200+ books. 

We'd been tracking our books and meetings in a shared Google Sheet, and honestly, that's all it needs.  Only two of us even care about tracking the data, unless we start a debate about whether or not we've read a given author or book. (After 20+ years of monthly meetings, these debates are more frequent)

A few years ago I created a Bookclub site (you can see the public-facing side of it here. Use the username 'guest' and no password), initially to serve my book club of 20 years and 200+ books. 

We'd been tracking our books and meetings in a shared Google Sheet, and honestly, that's all it needs.  Only two of us even care about tracking the data, unless we start a debate about whether or not we've read a given author or book. (After 20+ years of monthly meetings, these debates are more frequent)

Building the site was actually just an excuse to learn about a responsive CSS framework ... I think I started with Bootstrap but ended up on Bulma ... and a little Javascript.  And it got out of hand, as unplanned applications always do.  It had a home-grown login system, a private Manager interface, tie-ins to an Amazon Associate account, and a MySQL-based data model to support multiple book clubs.  In other words, complete overkill gathering dust on a virtual shelf.

"Book Club Mini" is a revised site that embraces a few simple truths:

  1. There are about 300 books ... this is not Big Data!
  2. A bookclub site usually doesn't need member management ... view the lists or don't.  It isn't secret stuff.
  3. If you commit to CSS and JavaScript, it will take over your whole life

The revised site is open to the public.  Books and Meetings are maintained in small, delimited text files.  Graphics are provided by Bookshop.org widgets.  PHP is used for setting up files and lists, and managing the server-side tasks.  And the look and feel leverages Lit.css, a 500 byte responsive CSS file, which is about all the CSS I'm willing to deal with.

A slightly more impressive subculture exists of Python and BASH scripts created to automate regular tasks:

  1. Scheduling meetings
  2. Looking up book ISBN identifiers and image files for book covers
  3. Creating backup spreadsheets that we all look at end-of-year for our annual Best and Worst meeting
  4. Updating supporting text files for the new site and the MySQL tables for the old one

Read More

The VIC20 Mystery

2024-07-12

I follow a channel called "The 8-bit Guy" on YouTube, run by vintage computer enthusiast David Murray. A couple years ago he was restoring a VIC20 PET computer that someone had found in the closet of a radio station.

The shipping box also contained a program cassette (!!) labeled "The All New K-Life Talkshow Prompter Program. Use at Your Own Discretion". I don't know what a talk show prompter program is, and Murray only looks at it briefly (he mostly wasn't interested, as he was engaged in restoring a computer). But he does recover the binary of the program from the tape and provides it for download in notes to the video.

I follow a channel called "The 8-bit Guy" on YouTube, run by vintage computer enthusiast David Murray. A couple years ago he was restoring a VIC20 PET computer that someone had found in the closet of a radio station.

The shipping box also contained a program cassette (!!) labeled "The All New K-Life Talkshow Prompter Program. Use at Your Own Discretion". I don't know what a talk show prompter program is, and Murray only looks at it briefly (he mostly wasn't interested, as he was engaged in restoring a computer). But he does recover the binary of the program from the tape and provides it for download in notes to the video.

I just had to know. What did this thing do?

Using a VIC20 emulator in Ubuntu, I was able to load the binary and run the program. I interupted it with some bad input (like every BASIC program I ever coded, this thing had next to no data validation logic) which dropped me to the BASIC command prompt. From there I could LIST the program, a few lines at a time.

In emulator-world I had no way to direct the LIST output to a file (none I could think of, anyway). But I could copy visible text to the Clipboard. So I slowly LISTed and copied, a few lines at a time, into the Clipboard, and then pasted that into a text file.

I then uploaded that whole ugly text file into a Claude.ai conversation and asked it to:

  1. clean up the text into a well formatted BASIC file
  2. Interpret the function of the program
  3. flowchart it
  4. provide a functional equivalent in Python

The flowchart was rendered in text as:

Main Loop:

The program runs in a continuous loop, waiting for user input to perform actions.

[Start]
  |
  v
[Initialize variables and set time]
  |
  v
[Main Loop]
  |
  +----> [Display current time and caller list]
  |         |
  |         v
  |      [Wait for input]
  |         |
  |         v
  |      [Process input]
  |         |
  |         +----> [Add new caller/segment]
  |         |
  |         +----> [Move to next caller]
  |         |
  |         +----> [Display attention message]
  |         |
  |         +----> [Show number of calls]
  |         |
  |         +----> [Handle special commands]
  |         |      (e.g., "go to break", "next guest")
  |         |
  |         +----> [End program]
  |
  +----> [Check time and update status]
          |
          v
[End or continue main loop]

After generating the Python rewrite, Claude.ai provides an overview of the script:

Claude.ai Analysis

This Python script replicates the core functionality of the VIC20 BASIC program:

  1. Time Management:
    • It sets a start time and allows setting a countdown.
    • It displays the current time and remaining time continuously.
  2. Caller/Segment Management:
    • You can add callers/segments with names and priorities.
    • It displays a list of callers and allows moving to the next caller.
  3. Special Commands:
    • It includes the special commands from the original program, displaying messages for each.
  4. Display Management:
    • It clears the screen for each update, mimicking the VIC20's screen clearing.
  5. Main Loop:
    • The program runs in a continuous loop, waiting for user input to perform actions.

To run this program, save it as a .py file and run it using Python. It will provide a text-based interface in the console, allowing you to manage a talk show similarly to the original VIC20 program.

Note that this Python version is more structured and object-oriented compared to the original BASIC program. It doesn't replicate the low-level memory operations or screen positioning of the VIC20, but it captures the essential functionality in a modern programming paradigm.


1 INPUT"SET TIME:";TI$
3 INPUT"COUNTDOWN CLOCK TIME";W
5 U=36879:IFGB=2THEN54
30 DIMN(75),N$(75):A=1:C=0:X=1:Z9=30:GOTO54
40 PRINT"[CLR]":IFO>1THEN54
45 GOSUB310
54 PRINT"[CLR]"
55 POKEU,8:FORA=XTOB:IFN(A)>0THENGOTO60
56 IFN(A)=0THENPRINT"[7 DOWN][5 RIGHT]ALL LINES CLEAR":GOTO70
60 PRINT"[HOME]";N$(A);TAB(19):PRINT"[LEFT]";N(A)
70 PRINT:NEXTA
72 IFO=8THEN4030
73 HI=0:RG=0:GP=8031:FORHI=1TO22:POKEGP+RG,100:RG=RG-1:NEXT
74 POKE8032,101:POKE8054,101:POKE8076,101
75 HI=0:RG=0:GP=8098:FORHI=1TO22:POKEGP+RG,99:RG=RG+1:NEXT
76 POKE8097,103:POKE8075,103:POKE8053,103
80 PRINT"[CLR][11 DOWN][11 RIGHT]";RIGHT$(TI$,4)
85 PRINT"[HOME][22 DOWN]";
90 PRINT"[RVS ON]";MID$(TI$,3,2);":";RIGHT$(TI$,2);"[RVS OFF]"
98 IFTI$="130000"THENTI$="010000"
99 GOSUB2585
100 GETO$:IFO$=""THEN95
101 IFO$="\"THENGOTO3010
102 O=VAL(O$):RT=1:IFO>9THEN100
106 ONOGOTO140,210,230,450,500,1500,1700,4000,5000
140 INPUT "[CLR][11 DOWN][RIGHT]\";N$
142 NA=NA+1
145 GETH$:IFH$=""THEN145
150 N=VAL(H$):IF N>5 THEN145
155 IFN=0THEN145
160 T=T+1:A=T:C=C+1:PRINT:PRINT"[PURPLE]":N(A)=N:N$(A)=N$
170 IF B=X+4THENX=X+1:NA=NA-1
180 B=B+1:GOTO40
210 IFNA=<0THEN100
220 PRINT"[PURPLE]":X=X+1:NA=NA-1:GOTO40
230 PRINT"[CLR][DOWN]":INPUT A$:GOTO600
310 POKEU,25:POKEU-1,10:POKEU-4,251
335 PRINT"[5 DOWN]     ATTENTION"
340 FORTX=1TO500:NEXT:POKEU-1,0
360 PRINT"[CLR]":RETURN
450 INPUT"[4 DOWN][RIGHT]# CALLS";C:PRINT"[2 DOWN]FREE:";FRE(0):FORTU=1TO2000:NEXT:GOTO40
500 POKE8097,181
505 GETS$:IFS$=""THEN505
579 IF S$="+"THEN 711
580 IF S$="D"THEN 731
581 IF S$="@"THEN 733
582 IF S$=":" THEN 734
584 IF S$="="THEN 730
585 IFS$="%"THEN1003
586 IFS$="&"THEN1005
587 IFS$="*"THEN1000
588 IFS$="^"THEN710
589 IFS$="_"THEN720
600 FOROP=1TO2:POKEU,12:FORTG=1TO1500:NEXT:POKEU,8:FORTF=1TO1000:NEXT:NEXT:POKE8097,103
610 GETX$:IFX$=""THEN600
620 GT=0:FG=0:FORGT=1TO44:POKE8163-FG,32:FG=FG+1:NEXT:GOTO100
710 PRINT"[CLR][2 DOWN]AIR TRAFFIC READY":GOSUB 600:GOTO 95
711 PRINT"[CLR][3 DOWN]DANGER WILL ROBINSON DANGER! BAD DOCTRINE!":GOSUB600:GOTO 95
720 PRINT"[CLR][4 DOWN]NEXT PROGRAM READY":GOSUB600:GOTO95
730 PRINT"[CLR][3 DOWN]ITS JUST ANOTHER CHRISTIAN TALKSHOW!":GOSUB 600:GOTO 95
731 PRINT"[CLR][4 DOWN]WAKE UP DAVE!!!!":GOSUB  600:GOTO 95
733 PRINT"[CLR][3 DOWN]":INPUT Q$:PRINT"[CLR]  [RVS ON]";Q$:GOSUB 600:GOTO 95
734 PRINT"[CLR][3 DOWN]LIGHTEN UP!":GOSUB 600:GOTO 95
1000 PRINT"[CLR][4 DOWN][RVS ON]   GO TO BREAK!!!   ":GOSUB600:GOTO40
1003 PRINT"[CLR][4 DOWN]NEXT GUEST ON LINE":GOSUB600:GOTO95
1005 PRINT"[CLR][4 DOWN]TIME TO GO!SHUT UP!":GOSUB600:GOTO95
1010 GOTO3037
1500 PRINT"[CLR]"
1510 GETG$:IFG$=""THEN1510
1520 GOTO40
1700 GB=2:GOTO2
2040 GOTO1010
2585 IFTI$=>"035555"THENGB=3
2587 IFTI$="035930"THEN3037
2590 RETURN
        


import time
import datetime
import os

class TalkShowManager:
    def __init__(self):
        self.callers = []
        self.current_index = 0
        self.start_time = None
        self.countdown_time = None
        self.gb = 0

    def clear_screen(self):
        os.system('cls' if os.name == 'nt' else 'clear')

    def set_time(self):
        self.start_time = datetime.datetime.now()
        print("Current time set.")

    def set_countdown(self):
        minutes = int(input("Enter countdown time in minutes: "))
        self.countdown_time = datetime.timedelta(minutes=minutes)

    def display_time(self):
        current_time = datetime.datetime.now()
        elapsed = current_time - self.start_time
        remaining = self.countdown_time - elapsed if self.countdown_time else None
        
        print(f"Current Time: {current_time.strftime('%H:%M:%S')}")
        if remaining:
            print(f"Time Remaining: {str(remaining).split('.')[0]}")

    def add_caller(self):
        name = input("Enter caller name: ")
        priority = int(input("Enter priority (1-5): "))
        self.callers.append({"name": name, "priority": priority})

    def display_callers(self):
        for i, caller in enumerate(self.callers[self.current_index:self.current_index+5], 1):
            print(f"{i}. {caller['name']} (Priority: {caller['priority']})")

    def next_caller(self):
        if self.current_index < len(self.callers) - 1:
            self.current_index += 1

    def special_command(self, command):
        messages = {
            "+": "DANGER WILL ROBINSON DANGER! BAD DOCTRINE!",
            "D": "WAKE UP DAVE!!!!",
            "@": lambda: input("Enter custom message: "),
            ":": "LIGHTEN UP!",
            "=": "ITS JUST ANOTHER CHRISTIAN TALKSHOW!",
            "%": "NEXT GUEST ON LINE",
            "&": "TIME TO GO! SHUT UP!",
            "*": "GO TO BREAK!!!",
            "^": "AIR TRAFFIC READY",
            "_": "NEXT PROGRAM READY"
        }
        
        message = messages.get(command)
        if callable(message):
            message = message()
        if message:
            print(message)
        time.sleep(2)

    def run(self):
        self.set_time()
        self.set_countdown()
        
        while True:
            self.clear_screen()
            self.display_time()
            print("\nCallers:")
            self.display_callers()
            
            print("\nCommands: (A)dd caller, (N)ext caller, (S)pecial command, (Q)uit")
            choice = input("Enter command: ").upper()
            
            if choice == 'A':
                self.add_caller()
            elif choice == 'N':
                self.next_caller()
            elif choice == 'S':
                command = input("Enter special command: ")
                self.special_command(command)
            elif choice == 'Q':
                break
            else:
                print("Invalid command")
            
            time.sleep(1)

if __name__ == "__main__":
    show = TalkShowManager()
    show.run()
        

Read More

Working with AI

2024-05-18

I haven't posted in forever!  Here is a progress report on a couple of efforts.

Like everybody on earth, I've been looking into the new AI tools.  In particular looking into the idea of getting some Pair Programming help.

I haven't posted in forever!  Here is a progress report on a couple of efforts.

Like everybody on earth, I've been looking into the new AI tools.  In particular looking into the idea of getting some Pair Programming help.

I like the scripting languages Perl, PHP, and more recently, Python.  I've gotten interested in Javascript mostly due to Daniel Shiffman's amazing Coding Train site and YouTube channel featuring the Javascript Processing framework for graphical applications.

But, scripting takes me a lot of time, so I was looking for help to speed things up and remove some of the drudgery.  I found that help, using the Agile idea of Pair Programming, and using an AI tool as my scripting partner.  So far I've worked with Claude.ai and ChatGPT.  I subscribe to both, so I'm getting functionality one step up from the free versions.

I've found that I can pose a query, usually starting with some framing ('Please be an experienced Python developer, and suggest a way to ...') and, as long as I provide a clear description of what I want, I get results back almost immediately.  In just a few minutes, I'm refining the scope or look of a working application, rather than working through a lot of setup.

I have learned a few things:

  1. Don't ask for too much at once ... big complicated descriptions get answers just as fast as smaller, focused asks, but are less likely to work/be what you want.
  2. Be prepared to test: these AI engines don't have the capability (yet) to run the code they propose, so that's your job.  And Mistakes Will Be Made.  Out of date libraries are suggested, datatype errors creep in ... normal, simple debugging stuff.
  3. The models love to talk.  Ask questions about how to test the code, or how to make it run faster. Upload the current script and ask the assistant to streamline it according to best practices.  Provide the text of error messages you see and ask for help identifying the source. They are tireless and constantly seem eager to help ...
  4. ... up to a point.  I've found that after a lot of back and forth around an intractable problem, the AI agent can almost seem to lose interest.  The answers start to generalize or repeat.  When this happens, you need to reframe, and start a new conversation

And always remember the trade-off ... struggling through problems is no fun, but it is often how you actually learn a new scripting or database language, so let these tools provide assistance, but don't let them become a crutch.

I've built all sorts of small applications using the AI as a pair programming partner, and it has saved me countless hours.  Some examples:

A genetic algorithm (GA) in Python, that tries to "evolve" a matching image for a source image from a sequence of rectangles and ellipses on the canvas.

Goal Boxes Boxes and
Ellipses

An automated stock trader ... this was a whole assembly of things: Python scripts to strip price data from Yahoo! Finance, run the trend and Fourier analysis for each member of the S&P 500 and store results and forecasts in SQL tables; a Bash script and cron job that repeated the whole production line weekly; and a notification engine that emailed weekly Buy and Sell alerts to the fund manager (me).  Truly impressive in all but its Actual Returns, which were phenomenal in 2024Q1 (just like everything else that quarter), and much less so afterwards.

A one-page Wordle Assistant application (Javascript).  Which, yes, is sort of cheating, but does save loads of time.

My Online Cookbook.  I can't cook much but got tired of managing a bunch of disorganized printouts in a folder.  Far easier to prop up my iPad Mini and work from that.  This is a simple PHP web site.  The recipes are added as XML files in an online folder.  I have to do some work to improve the images assigned to the recipes, and there is always transcribing to do, but this has been a very useful tool!

Read More


Society of Books

2024-07-13

The VIC20 Mystery

2024-07-12

Working with AI

2024-05-18


Puzzle Solvers
Wordle Assistant
Word Mining
Cryptogram Creator
Cryptogram Solver
Sudoku Solver
Random Cartoon