-
Apr 12, 2018
Spreadsheet Jeopardy!
My girlfriend and I often watch Jeopardy! together, and we play along and keep score to make it more fun.
To avoid having to pause the show to wager, we have a house rule that “Daily Double”s are just worth twice their marked value. E.g., a D.D. on a $600 clue would be worth $1200. And we yell the answers (questions?) instead of buzzing in.
After tallying a number of games by hand, I decided to make a custom function in Google Sheets to keep score. Our scoring notation is:
- correct: first initial of first name
- incorrect: first initial of last name
- daily double: initial followed by asterisk
function score(results, level) { mScore = 0; kScore = 0; results[0].forEach( function(cell) { cell = cell.toLowerCase() if (cell.match(/.*m\*.*/)) { mScore += 2 * level; } else if (cell.match(/.*m.*/) !== null) { mScore += level; } else if (cell.match(/.*b\*.*/)) { mScore -= 2 * level; } else if (cell.match(/.*b.*/)) { mScore -= level; } if (cell.match(/.*k\*.*/)) { kScore += 2 * level; } else if (cell.match(/.*k.*/)) { kScore += level; } else if (cell.match(/.*c\*.*/)) { kScore -= 2 * level; } else if (cell.match(/.*c.*/)) { kScore -= level; } }) return [[mScore, kScore]]; }
(Yes, I’m aware this could be refactored)
It just occurred to me that I could probably modify this function to gift me a stray hundred dollars every now and then. She probably wouldn’t notice… 😈
-
Apr 9, 2018
Setting Emacs Theme Based on Ambient Light
I sit next to a window at work. On sunny days it’s easier to see a light editor theme, and when the sky is dark, a dark theme is easier on my eyes. So I decided to try to have my MacBook automatically switch the Emacs theme based on readings from the ambient light sensor.
There are two parts to this solution: a command-line executable to read data from the sensor, and then a small elisp function to do the theme switching.
The program below is from StackOverflow and slightly modified. It gets the
AppleLMUController
IO service, then, when the service is ready, prints the light sensor data to stdout and exits.// lmutracker.mm // // clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation #include <mach/mach.h> #import <IOKit/IOKitLib.h> #import <CoreFoundation/CoreFoundation.h> static double updateInterval = 0.1; static io_connect_t dataPort = 0; void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) { kern_return_t kr; uint32_t outputs = 2; uint64_t values[outputs]; kr = IOConnectCallMethod(dataPort, 0, nil, 0, nil, 0, values, &outputs, nil, 0); if (kr == KERN_SUCCESS) { printf("%8lld", values[0]); exit(0); } if (kr == kIOReturnBusy) { return; } mach_error("I/O Kit error:", kr); exit(kr); } int main(void) { kern_return_t kr; io_service_t serviceObject; CFRunLoopTimerRef updateTimer; serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController")); if (!serviceObject) { fprintf(stderr, "failed to find ambient light sensors\n"); exit(1); } kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort); IOObjectRelease(serviceObject); if (kr != KERN_SUCCESS) { mach_error("IOServiceOpen:", kr); exit(kr); } setbuf(stdout, NULL); updateTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + updateInterval, updateInterval, 0, 0, updateTimerCallBack, NULL); CFRunLoopAddTimer(CFRunLoopGetCurrent(), updateTimer, kCFRunLoopDefaultMode); CFRunLoopRun(); exit(0); }
The accompanying elisp code will invoke that executable on a timer and change the theme based on the light reading.
(setq current-theme "dark") (defconst light-theme 'majapahit-light) (defconst dark-theme 'majapahit-dark) ;; will apply a dark theme if the room is dark, and a light theme if the room is ;; bright (defun change-theme-for-lighting () (let* ((current-light-sensor-reading (string-to-number (shell-command-to-string "./lmutracker")))) (if (< current-light-sensor-reading 100000) (when (not (string-equal current-theme "dark")) (load-theme dark-theme 1) (setq current-theme "dark")) (when (not (string-equal current-theme "light")) (load-theme light-theme 1) (setq current-theme "light"))))) ;; probably want to run this less frequently than every second (run-with-timer 0 1 #'change-theme-for-lighting)
Edit 4/7/2019: Read a Russian translation of this post here.
-
Apr 2, 2018
Creating a new blog post in Emacs
Now that my blog is based on markdown text files, some new tooling options have opened!
- entire blog is under source control (done)
make
targets for common actions (e.g. create new post, deploy, serve dev version) (done)- git hooks for publishing (need to think about this more)
- blogging via emacs! (done!)
I just wrote this function to create a new posts file and open a buffer for editing it. In fact, it’s how I’m editing this post right now!
(defun new-blog-post () (interactive) (let ((post-title (read-string "Enter new post title: "))) (let* ((posts-dir "/ssh:vps:~/projects/blog/_posts/") (clean-title (replace-regexp-in-string "[^[:alpha:][:digit:]_-]" "" (s-replace " " "-" (downcase post-title)))) (new-post-filename (concat (format-time-string "%Y-%m-%d") "-" clean-title ".md")) (frontmatter-template "---\nlayout: post\ntitle: {title}\ndate: {date}\n---\n\n") (frontmatter (s-replace "{date}" (format-time-string "%Y-%m-%d %H:%m %z") (s-replace "{title}" post-title frontmatter-template))) (new-post-file (expand-file-name new-post-filename posts-dir))) (if (file-exists-p new-post-file) (message "A post with that name already exists.") (write-region frontmatter nil new-post-file) (find-file new-post-file)))))
This was also the first elisp function I wrote :)
-
Mar 31, 2018
under construction
I was having some headaches with my existing shared hosting provider, so I decided to move this site to a VPS. I also didn’t want to run a LAMP stack, so I’ve moved this blog from WordPress to Jekyll. Please pardon the appearance as I find time to iron things out.
-
Feb 8, 2018
How to set up free SSL on shared-hosting with Let’s Encrypt
I just updated this domain to use HTTPS with Let’s Encrypt as a certificate authority. Presently this site is on a shared-hosting provider and I had to generate a cert manually and then upload it. Here are instructions for doing that.
Note: some shared-hosting providers may offer a way to automatically generate and install a Let’s Encrypt (or other CA) certificate directly through the cPanel. I’d recommend doing that if it’s an option :)
First, download and install
certbot
. On a separate computer (i.e., not the website host), runcertbot
to generate a certificate:brew install certbot mkdir ~/letstencrypt && cd ~/letstencrypt/ certbot --config-dir . --work-dir . --logs-dir . certonly --manual
After displaying some prompts,
certbot
will produce a challenge string and ask you to upload a file to your host containing that content (using thehttp
challenge). This is to prove control of the website.On the host, create the file as instructed. E.g. copy the challenge text, then:
pbpaste > challengefile ssh myhost 'mkdir -p ~/public_html/.well-know/acme-challenge/' scp challengefile myhost:~/public_html/.well-known/acme-challenge/rPs-CyPusl...
Then, confirm you’ve uploaded the file and complete the
certbot
setup to create the certificate. There will be alive
directory containing the generated certificate and secret.live └── my-website.com ├── README ├── cert.pem -> ../../archive/my-website.com/cert1.pem ├── chain.pem -> ../../archive/my-website.com/chain1.pem ├── fullchain.pem -> ../../archive/my-website.com/fullchain1.pem └── privkey.pem -> ../../archive/my-website.com/privkey1.pem
Copy the contents of
fullchain.pem
and paste them into the certificate text box of your cPanel’s SSL configuration settings or upload the certificate file directly.Finally, install the certificate and upload
privkey.pem
.Once the process is complete the challenge file can be removed from the server. You should now be able to access your domain over
https
.
Recent Posts
- Agentic git bisect
- MacWhisper
- Coding with agent helper scripts
- Vibecoding’s allure: the inventor and the fiend
- Philadelphia Data Explorer
- Plant identification
- Let’s Flip an Unfair Coin
- Flight Focus
- Generative AI Accelerates Our Exploration of the Search Space
- AI Assistant Use-Case: Performance Feedback