A delightful journey in programming
I am reminded what a giant nerd I am since I watched this while working on other very nerdy things.
Podcast Middleman Dev
Hammered the majority of the podcast middleman out this morning thanks to Python. A few things still to fix (like the fact The Daily has a 12.5 meg RSS file and I am not looking to archive their entire backlog) but the core functionality is there and it's just about trimming off what I don't locally archive, etc.
Never Stop Blowing Up's Dice System
So I decided to do some math & coding this morning. I was curious on the distribution of numbers for the exploding dice system used in Dropout's new actual play series: Never Stop Blowing Up
They are using a system built on the Kids on Bikes system, modified to serve the game's theming. The main difference (based on what we've seen) is the dice rolling system.
For Kids on Bikes, you roll the same die when it explodes. For Never Stop, you start with a D4, then on a max roll, you roll again on the next sized dice (D4->D6->D8->D10->D12->D20) adding the combined values of rolls. Roll 1, 2, 3 that's what you get, on a 4, you add 1d6, etc
There's a few more details to what Never Stop is doing, like that it makes it quasi-leveling for the players. Once you explode to a d6, that is now your starting dice roll on that stat, etc. I'm not taking that into consideration for this morning's exploration on the math.
So, looking at every roll beginning with a D4, you have a 25% chance on rolling 1, 2 or 3. Then the remaining 25% chance gets distributed across exploding rolls.
| % | Range |
|---|---|
| 25% | 1-3 |
| 4.17% | 5-9 |
| 0.52% | 11-17 |
| 0.05% | 19-27 |
| 0.04% | 29-39 |
| 0.002% | 41-60 |
You can never roll a 4, 10 (4+6), 18 (4+6+8), 28 (4+6+8+10), 40 (4...12) because those are the threshold numbers an you always add at least 1 to those numbers. This essentially means that everything after these thresholds uses that slot's percentage.
So with 1d4, you have a 25% chance of getting a 4. But since there is no 4, the remaining range of numbers all occupy that 25%. Adding the 1d6 means that you have 25%/6 on each possible roll, except on a 6, that is the percentile chance of the 1d8, etc.
So that's the part I logicked through the math of without any code or simulation. I decided to code a Python script to do large number simulations to see how many times I would roll each number.
from random import randint
dice = [4, 6, 8, 10, 12, 20]
def roll(sides):
r = randint(1,sides)
if r == sides and r < 20:
n = dice.index(sides) + 1
return r + roll(dice[n])
return r
def batchroll():
rolls = []
for i in range(10000):
rolls.append(roll(4))
out = ""
for i in range(1,61):
out = out + str(rolls.count(i)) + "\t"
out = out + "\n"
with open("rolls.txt", "a") as text_file:
text_file.write(out)
for x in range(10000):
batchroll()
(Don't judge the code, I'm sure it could be improved.)
First off, after running it - I was glad to see that I had logicked it all out correctly and the rolls matched expectations. Secondly, seeing actual distribution of numbers was useful for perspective. It's one thing to see them as abstract percentiles, but it's another to see actual numbers of instances.
Out of nearly 100 million simulated rolls, I only rolled on the d20 ~4,300 times. Meaning that with 100,000,000 rolls; I rolled the max of "60" (4+6+...+20) just 203 times. Here's the results of the simulated rolls on a Google Sheet.
Interesting stuff mathematically. A fun morning exercise for the ol' noggin.
Update on last night's 'Wikindle' code
Code ran without issue, though the formatting was slightly off.
This morning I hammered out the code to grab the top 100 most popular entries from yesterday and add them to the archive. I'm not sure how useful that will actually be, a lot of those entries are pop culture (am I really going to need an entry about the new Kraven Marvel series?) But we'll see. It isn't like this is a major space hog.
Last night snagged roughly 8,000 entries and it took up 250 megs. Plenty of space.
One thing which is lost in this process is any cross linking. I'd love to go through and add that back in, or even better figure out how to best avoid that bring stripped out from the start. We'll see. In any case, a fun diversion to distract me.
Wikipedia on my Shelf Progress
At this moment my PC is downloading nearly 10,000 entries from Wikipedia as part of my idea of a locally hosted version of the encyclopedia. I'm making use of the Vital articles project, specifically level 4, which is roughly ten thousand entries on various topics.
I cobbled together a python script to pull from the API, parse the HTML to markdown, strip any lingering tags (such as spans, abbrs, etc.)
It isn't perfect, no images from the entries are brought over. I'll work on that further in a future iteration.
Not sure what to call this project. I called the folder "Wikindle" as a smerging of "wikipedia" and "kindle" but I don't love that name. I'll play around with it.
I also have an idea for this to be a "living" archive. Where perhaps it is a cron script which runs nightly for that day's top X entries, and snags them, accruing more and more notable content over time. Obviously quality will vary, but we'll see how it goes. Then, every few months, I update the Kindle.
Lastly, my observation as I work on code this evening. It remains comforting that ChatGPT struggles with some very basic coding concepts. I know lots of people worry LLMs will lead to the end of programmers but that simply isn't true as far as I can see. At least, not without more massive steps forward.
Coding ChatGPT from Scratch
Absolutely loving this step by step walk through explaining the terminology and how exactly it all works.
Python spelling correction deep dive
Ran across this old article about how Google does their spelling correction on searches. You search for 'speling' and they say 'Returning results for 'spelling'' - It's a bit of a deep dive for anyone who isn't proficient with programming.
It's Alive!
After roughly three hours of coding I have a 130 line Python script that will snag the videos on my "To Watch" YouTube playlist, convert the files to mp3s, generate a podcast XML file, and then upload them all to my server.
Now I can use a podcast app while I'm driving and listen to these files. I had a way of listening to them while I drove but it was a process which required me picking up and interacting with my phone, it didn't integrate with Android Auto. This script will now allow that.
This is definitely an MVP hacky solution. But, it's a hacky solution that gets the job done. As of now, I'll just run it from my laptop. No need to set this up on the server as an automated script. The frequency is such that I add 1-3 videos to the list in a week. Right now it has 19 videos on it. So plenty of backlog and I can probably just do it as a weekly task and I'll be fine.
Also, jeez, I forget how easy Python is to work in. I do the majority of my web coding in PHP, but it was nice to lean heavily on the existing Python libraries for this one.
I continue my efforts to learn Python, tonight's project was to implement a calendar function. Python has a built in tool that does this, but it's about learning the language and implementing the tools for it. I was able to do it in about 60 minutes, with most of that being troubleshooting errors and learning some of the requirements for Python.
As I come from PHP, getting used to strict typing on variables is a big learning for me.
One of the common pitfalls I fall into is naming my files an obvious filename, which ends up being also something I import. For example, tonight's file was originally called 'calendar.py' but since I am importing the calendar library in python, it was erroring. So it got renamed to cal.py.
Here's the code I came up with:
import calendar
from datetime import datetime
date = input("Enter Date: ")
dt = datetime.strptime(date,"%m/%d/%Y")
header = datetime.strftime(dt,"%B %Y")
weekday = int(datetime.strftime(dt,"%w"))
date = int(datetime.strftime(dt,"%-d"))
month = int(datetime.strftime(dt,"%-m"))
year = int(datetime.strftime(dt,"%Y"))
firstDateOfMonth = int(datetime.strftime(datetime.strptime(str(month)+"/1/"+str(year),"%m/%d/%Y"),"%w"))
lengthOfMonth = int(calendar.monthrange(int(year),int(month))[1])
print(header)
print("[Sun][Mon][Tue][Wed][Thu][Fri][Sat]")
line = ""
if firstDateOfMonth > 0:
k = 0
while k 6:
print(line)
line = ""
k = 0
filler = " "
if i == date:
filler = "*"
if i This code outputs a simple text calendar such as the following:
Python online Textbook
Now that I've gotten a few Python projects under my belt by process of code segments and rough guesses, I figured it was time I give a book a read and learn what else Python holds in store for me.
Simple Tic-Tac-Toe in Python
My learning of Python continued today with me coding a simple Tic-Tac-Toe client. It is very dumb, the computer always plays randomly and currently has no intelligence to it. But I wrote it from scratch with no outside resource or code segments. Doing things like this is always a great feeling for me, learning something is one of my favorite things to do.
Here is my Python code:
from random import randint
board = [1,2,3,4,5,6,7,8,9]
legalplays = [1,2,3,4,5,6,7,8,9]
def drawboard(board):
print(board[0],"|",board[1],"|",board[2])
print("---------")
print(board[3],"|",board[4],"|",board[5])
print("---------")
print(board[6],"|",board[7],"|",board[8])
def checkboard(board):
patterns = [[0,1,2],
[3,4,5],
[6,7,8],
[0,3,6],
[1,4,7],
[2,5,8],
[0,4,8],
[2,4,6]]
for pattern in patterns:
if board[pattern[0]] == board[pattern[1]] and board[pattern[0]] == board[pattern[2]]:
return board[pattern[0]]
return 0
def computerPlay(board):
availableplays = []
for i in board:
if i in legalplays:
availableplays.append(i-1);
moves = len(availableplays)
move = randint(0,moves-1)
return availableplays[move]
playfirst = randint(1,2)
if playfirst == 1:
#Computer Plays First
play = randint(0,8)
board[play] = "O";
noresult = True
while noresult:
drawboard(board)
player = input("Choose where to put your X: ")
if player.isnumeric():
player = int(player) - 1
if player
I am working on learning Python as a programming language and getting used to it. I sat down and coded "Rock, Scissors, Paper, Lizard, Spock" - First I did the endless if, elif, else statements then decided to go for a more compact solution via a dense multidimensional array.
from random import randint
t = [["Rock",[0,-1,1,1,-1],["ties","is covered by","smashes","crushes","is thrown by"]],
["Paper",[1,0,-1,-1,1],["covers","ties","is cut by","is eaten by","disproves"]],
["Scissors",[-1,1,0,1,-1],["smashed by","cuts","ties","decapitates","is used by"]],
["Lizard",[-1,1,-1,0,1],["is crushed by","eats","is decapitated by","ties","poisons"]],
["Spock",[1,-1,1,-1,0],["throws","is disproven by","uses","is poisoned by","ties"]]]
#assing a random play to computer
computerindex = randint(0,4)
player = False;
while player == False:
player = input("Rock, Paper, Scissors, Lizard, Spock? ")
noresult = True
for entry in t:
if entry[0] == player:
if entry[1][computerindex] > 0:
noresult = False
print("You win! ",player,entry[2][computerindex],t[computerindex][0])
elif entry[1][computerindex]
