Google v Oracle: What happened and what it means

On April 7, 2021, in General, by Neil Stevens

On April 5, 2021, the Supreme Court handed down its opinion in Google LLC v Oracle America, Inc. Almost all of the reporting on this is terrible because the journalists covering it lack understanding of copyright and of programming. I understand both, so let me explain what’s going on here.

Continue reading »

Tagged with:
 

Reimagining the Marvel Civil War

On August 31, 2018, in General, by Neil Stevens

INTERIOR OF STARK TOWER

Tony Stark stands in a board room, holding a piece of paper. Entering the glass walled room is Captain America, in his costume. Both men sit, each at one side of the center of the table, across from each other.

TONY STARK

Have a seat, Rogers. I know you’re angry about the SRA. So am I? Regulation, am I right? So I brought you in to show you that this isn’t a big deal.

CAPTAIN AMERICA

Liberty is always a “big deal,” Stark.

TONY STARK

See, Rogers, that’s exactly what I’m talking about. You’re making a mountain out of a molehill. This is just like the DMV. Did kids drive cars in the 40s? It’s just a form. It’s just a piece of paper.

CAPTAIN AMERICA

It’s the first step towards something more. I saw that in Germany in the 40s.

Tony Stark pushes the piece of paper face up, toward Captain America.

TONY STARK

Come on Rogers. Steve. This isn’t the second coming of Adolf Hitler. Steve, it’s just a harmless government form. May I call you Steve? Steve, just take this form, fill it out, and turn in it. That’s all you have to do. Turn in the damn form.

Captain America stands, goes to the door, and stops to look at Tony.

CAPTAIN AMERICA

Tony, you want the form? Come and take it.

Tagged with:
 

Why I still play Hearthstone

On December 6, 2017, in General, by Neil Stevens

Why do I still play Hearthstone despite regularly complaining about how the designers have stripped so much skill from the game? Because sometimes, just sometimes, I get to outplay my opponent hard and earn a win.

Continue reading »

 

I love to support NES Zelda tournaments. To date there have been four and I’ve joined all of them. However because of some rules decisions I’m not joining the Legend of Zelda Randomizer Tournament 2. While as a player I’m disappointed, as a community builder and tournament organizer, I’m glad of this development.

Continue reading »

 

Lessons learned from the 2016 Zelda II All Keys Tournament

On December 30, 2016, in General, by Neil Stevens

We had the 2016 Zelda II 100% All Keys Tournament over the last 7 and a half weeks. It was the second annual event, and both of them were won by Simpoldood. I think it was a great success, but we still have more to learn on how to do these well.

Continue reading »

 

A list of the most influential conservatives

On February 17, 2016, in General, by Neil Stevens

Dan McLaughlin was talking about the most influential conservatives of his lifetime. So I got to thinking about who I think the most influential modern conservatives were.

It’s not 100% “in my lifetime” as Goldwater’s direct influence had waned substantially by the time I was born, but the effects of his Presidential run were still being felt.

The list, in order:

  • Reagan – Presidency changed the GOP
  • Buckley – Founded the modern conservative movement
  • Scalia – His wit inspired countless conservative legal minds
  • Goldwater – Presidential run grew what Buckley started
  • Rehnquist – The Lone Ranger paved the way for Scalia
  • Gingrich – Shrank the government in a way few ever have
  • DeMint – Forerunner of the TEA party anti-establishment revolt
  • Erickson – Champion of the TEA party revolt, outsider’s voice
  • Helms – Senator No, tireless warrior in the Senate
  • Thomas – Much quieter than Scalia, but heroically withstands the left’s arrows
 

World Cup Zelda 2 Tournament

On October 28, 2015, in General, by Neil Stevens

You can’t please everyone with any tournament structure. No matter what you do, there are going to be unbalanced matchups, or people who absolutely cannot win the tournament.

A straight double-elimination, Personal Best-seeded tournament (such as used in the Zeldaone tournament) I think works very well once you get over 32 people. I think it gives the most people a genuine shot to win a game, while still giving the top runners a showdown at the end.

However for reasons I won’t go into (I’m not going to put words in people’s mouths), this is a tournament structure that is disliked by many of the top Zelda 2 runners. So, in trying to find a compromise structure, I outlined a World Cup of Zelda 2. Here’s how it works.

Continue reading »

 

My clock is done

On October 26, 2015, in General, by Neil Stevens

It’s minimalist, but it’s done.

Clock Completed

I posted this earlier on Twitter but after that I decided I needed to tape up the set of plugs connecting the display for ease of plugging/unplugging. That turned out to matter when I realized I’d assembled the case inside out, weirdly enough. So I had to take it all apart and put it back together, which meant unplugging and replugging the display!

I ended up putting no buttons on it. I wrote a little python program for setting the time.

Following is the final code for the Arduino sketch, which drives the display, which is a row of four 8×8 LED grids, driven by an array of four MAX7219 chips. The code also drives the DS3231-based Real Time Clock daughterboard with battery backup.

Though while I’m listing components, the case is a neat little acrylic thing, and the main board is a SainSmart Arduino Uno clone.

On the software end I used the Bounce2, LedControl, and RTClib libraries, with RTClib depending on the Wire library. Well, I had use Bounce2 until I took out the buttons, then just commented all of that out.

/*
#include "Bounce2.h"
*/
#include "LedControl.h"
#include "RTClib.h"
#include "Wire.h"

const byte lowA[8] = {B00000000, B00000000, B00000000, B01111100, B10000010, B10000010, B10000010, B01111101};
const byte capB[8] = {B11111110, B10000001, B10000001, B10000001, B11111110, B10000001, B10000001, B11111110};
const byte lowE[8] = {B00000000, B00000000, B00000000, B01111110, B10000001, B11111111, B10000000, B01111110};
const byte lowG[8] = {B00000000, B00000000, B01111110, B10000001, B10000001, B01111110, B00000001, B01111110};
const byte capH[8] = {B10000001, B10000001, B10000001, B11111111, B10000001, B10000001, B10000001, B10000001};
const byte lowR[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B10000000, B10000000, B10000000};
const byte lowS[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B01111110, B00000001, B01111110};
const byte lowU[8] = {B00000000, B00000000, B00000000, B10000001, B10000001, B10000001, B10000011, B01111101};
const byte capW[8] = {B10000001, B10000001, B10000001, B10010001, B10010001, B10010001, B10010001, B01111110};
const byte space[8] = {0, 0, 0, 0, 0, 0, 0, 0};

const byte digits[10][8] = {
	{ B0111110, B1100011, B1100111, B1101011, B1110011, B1100011, B0111110, 0},
	{ B0001110, B0011110, B0001110, B0001110, B0001110, B0001110, B0001110, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0110000, B1100000, B1111111, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0000011, B1100011, B0111110, 0},
	{ B1100110, B1100110, B1100110, B1100110, B1111111, B0000110, B0000110, 0},
	{ B1111111, B1100000, B1111110, B0000011, B0000011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100000, B1111110, B1100011, B1100011, B0111110, 0},
	{ B1111111, B0000011, B0000110, B0001100, B0011100, B0011100, B0011100, 0},
	{ B0111110, B1100011, B1100011, B0111110, B1100011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100011, B0111111, B0000011, B1100011, B0111110, 0}
};

const unsigned twentyFourHour = false;
LedControl lc = LedControl(11 /* data */, 12 /* clk */, 10 /* cs */, 4);
RTC_DS1307 RTC;
/*
Bounce brightnessButton = Bounce();
Bounce *buttonArray[1] = {&brightnessButton};
*/
boolean firstLoop = true;

void clearAll(LedControl lc) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.clearDisplay(i);
	}
}

void setAll(LedControl lc, const int x, const int y)
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setRow(i, x, y);
	}
}

void writeLetter(LedControl lc, const int digit, const byte letter[8], const boolean colon = false)
{
	for(int i = 0; i < 8; ++i) {
		int bits = letter[i];
		if(colon && (i == 2 || i == 4)) {
			bits |= B10000000;
		}
		lc.setRow(digit, i, bits);
	}
}

void setIntensity(LedControl lc, const int brightness) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setIntensity(i, brightness);
	}
}

void setup()
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.shutdown(i, false);
		lc.setIntensity(i, 0x0f);
		lc.clearDisplay(i);
	}

/*
	pinMode(1, INPUT_PULLUP);
	brightnessButton.attach(1);
*/

	writeLetter(lc, 3, capB);
	writeLetter(lc, 2, lowA);
	writeLetter(lc, 1, lowG);
	writeLetter(lc, 0, lowU);

	Serial.begin(9600);

	RTC.begin();
}

// returns array {hourTens, hourOnes, minuteTens, minuteOnes}
const byte *timeDigits(DateTime now) {
	static byte ret[4] = {9,9,9,9};
	byte hour = now.hour();
	if(twentyFourHour) {
		hour %= 24;
	} else {
		hour %= 12;
		if(hour == 0) {
			hour = 12;
		}
	}
	byte min = now.minute();

	ret[0] = hour / 10;
	ret[1] = hour % 10;
	ret[2] = min / 10;
	ret[3] = min % 10;
	return ret;
}

void loop()
{
	while(!RTC.isrunning()) {
		Serial.println("RTC not running.");
	}
	static unsigned short brightness;

	if(firstLoop) {
		brightness = 0xf;
		firstLoop = false;
	}

	while(Serial.available() > 0) {
		String cmd = Serial.readString();
		cmd.trim();
		if(cmd == "SET") {
			Serial.println("Reading date");

			while(Serial.available() < = 0) {
				delay(1);
			}
			String d = Serial.readString();
			d.trim();

			Serial.print("Setting date to '");
			Serial.print(d.c_str());
			Serial.println("'");

			Serial.println("Reading time");

			while(Serial.available() <= 0) {
				delay(1);
			}
			String t = Serial.readString();
			t.trim();

			Serial.print("Setting time to '");
			Serial.print(t.c_str());
			Serial.println("'");

			RTC.adjust(DateTime(d.c_str(), t.c_str()));
		} else if(cmd == "SEIZE") {
			Serial.println("YOU");
		}
	}

/*
	for(byte i = 0; i < 2; ++i) {
		buttonArray[i]->update();
	}

	if(brightnessButton.fell()) {
		brightness = (brightness + 1) % 0x10;
		setIntensity(lc, brightness);
	}
*/

	DateTime now = RTC.now();
	const byte *currentDigits = timeDigits(now);

	// Write the clock
	if(currentDigits[0] == 0) {
		writeLetter(lc, 3, space);
	} else {
		writeLetter(lc, 3, digits[currentDigits[0]]);
	}
	writeLetter(lc, 2, digits[currentDigits[1]]);
	writeLetter(lc, 1, digits[currentDigits[2]], (now.second() % 2) == 1);
	writeLetter(lc, 0, digits[currentDigits[3]]);
}
 

Clock update

On October 25, 2015, in General, by Neil Stevens
So I decided to be lazy and just have my clock’s interface be a computer program:
import datetime
import glob
import serial
import sys
import time

if len(sys.argv) > 1:
	port = sys.argv[1]
else:
	port = glob.glob('/dev/tty.usbmodem*')[0]

print "Writing to %s" % port

arduino = serial.Serial(port, 9600)
time.sleep(3)
arduino.write("SEIZE\n")

result = arduino.readline().strip()
if not result == "YOU":
	print("Clock not found\n")
	sys.exit(1)

print("Clock found, setting to current time.")

arduino.write("SET\n");
print(arduino.readline().strip());

arduino.write("%s\n" % time.strftime('%b %d %Y'));
print(arduino.readline().strip());

arduino.write("%s\n" % time.strftime('%H:%M:%S'));
print(arduino.readline().strip());
 

So I built a clock

On October 6, 2015, in General, by Neil Stevens

Here’s a video of it in action:

Parts used:

Libraries used:

My code:

#include "Bounce2.h"
#include "LedControl.h"

const byte lowA[8] = {B00000000, B00000000, B00000000, B01111100, B10000010, B10000010, B10000010, B01111101};
const byte capB[8] = {B11111110, B10000001, B10000001, B10000001, B11111110, B10000001, B10000001, B11111110};
const byte lowE[8] = {B00000000, B00000000, B00000000, B01111110, B10000001, B11111111, B10000000, B01111110};
const byte lowG[8] = {B00000000, B00000000, B01111110, B10000001, B10000001, B01111110, B00000001, B01111110};
const byte capH[8] = {B10000001, B10000001, B10000001, B11111111, B10000001, B10000001, B10000001, B10000001};
const byte lowR[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B10000000, B10000000, B10000000};
const byte lowS[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B01111110, B00000001, B01111110};
const byte lowU[8] = {B00000000, B00000000, B00000000, B10000001, B10000001, B10000001, B10000011, B01111101};
const byte capW[8] = {B10000001, B10000001, B10000001, B10010001, B10010001, B10010001, B10010001, B01111110};
const byte space[8] = {0, 0, 0, 0, 0, 0, 0, 0};

const byte digits[10][8] = {
	{ B0111110, B1100011, B1100111, B1101011, B1110011, B1100011, B0111110, 0},
	{ B0001110, B0011110, B0001110, B0001110, B0001110, B0001110, B0001110, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0110000, B1100000, B1111111, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0000011, B1100011, B0111110, 0},
	{ B1100110, B1100110, B1100110, B1100110, B1111111, B0000110, B0000110, 0},
	{ B1111111, B1100000, B1111110, B0000011, B0000011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100000, B1111110, B1100011, B1100011, B0111110, 0},
	{ B1111111, B0000011, B0000110, B0001100, B0011100, B0011100, B0011100, 0},
	{ B0111110, B1100011, B1100011, B0111110, B1100011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100011, B0111111, B0000011, B1100011, B0111110, 0}
};

const unsigned long maxTicks = 0xFFFFFFFF;
const unsigned twentyFourHour = false;
LedControl lc = LedControl(11, 12, 10, 4);
Bounce hourButton = Bounce();
Bounce minuteButton = Bounce();
Bounce tenMinuteButton = Bounce();
Bounce brightnessButton = Bounce();
Bounce *buttonArray[4] = {&hourButton, &minuteButton, &tenMinuteButton, &brightnessButton};
boolean firstLoop = true;

void clearAll(LedControl lc) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.clearDisplay(i);
	}
}

void setAll(LedControl lc, const int x, const int y)
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setRow(i, x, y);
	}
}

void writeLetter(LedControl lc, const int digit, const byte letter[8], const boolean colon = false)
{
	for(int i = 0; i < 8; ++i) {
		int bits = letter[i];
		if(colon && (i == 2 || i == 4)) {
			bits |= B10000000;
		}
		lc.setRow(digit, i, bits);
	}
}

void setIntensity(LedControl lc, const int brightness) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setIntensity(i, brightness);
	}
}

void setup()
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.shutdown(i, false);
		lc.setIntensity(i, 0);
		lc.clearDisplay(i);
	}

	pinMode(1, INPUT_PULLUP);
	brightnessButton.attach(1);
	pinMode(2, INPUT_PULLUP);
	hourButton.attach(2);
	pinMode(3, INPUT_PULLUP);
	tenMinuteButton.attach(3);
	pinMode(4, INPUT_PULLUP);
	minuteButton.attach(4);
}

// returns array {hourTens, hourOnes, minuteTens, minuteOnes}
const byte *timeDigits(const unsigned long seconds) {
	static byte ret[4] = {9,9,9,9};
	byte hour = seconds / 3600;
	if(twentyFourHour) {
		hour %= 24;
	} else {
		hour %= 12;
		if(hour == 0) {
			hour = 12;
		}
	}
	byte min = (seconds % 3600) / 60;

	ret[0] = hour / 10;
	ret[1] = hour % 10;
	ret[2] = min / 10;
	ret[3] = min % 10;
	return ret;
}

unsigned long secondsFromTime(byte hourTens, byte hourOnes, byte minuteTens, byte minuteOnes)
{
	return hourTens * 36000 +
	       hourOnes * 3600 +
	       minuteTens * 600 +
	       minuteOnes * 60;
}

void loop()
{
	boolean reset = false;
	static unsigned long ticks;
	static unsigned long offsetSeconds;
	static unsigned long lastSeconds;
	static unsigned long offsetTicks;
	static unsigned long lastTicks;
	static unsigned short brightness;

	ticks = millis();

	if(hourButton.fell()) {
		const byte *currentDigits = timeDigits(lastSeconds % 86400);
		long hours = currentDigits[0] * 10 + currentDigits[1];
		hours += 1;
		if(twentyFourHour) {
			hours %= 24;
		} else {
			hours %= 12;
			if (hours == 0) {
				hours = 12;
			}
		}
		offsetSeconds = secondsFromTime(hours / 10,
		                                hours % 10,
		                                currentDigits[2],
		                                currentDigits[3]) % 86400;
		reset = true;
	}

	if(tenMinuteButton.fell()) {
		const byte *currentDigits = timeDigits(lastSeconds % 86400);
		offsetSeconds = secondsFromTime(currentDigits[0],
		                                currentDigits[1],
		                                (currentDigits[2] + 1) % 6,
		                                currentDigits[3]) % 86400;
		reset = true;
	}

	if(minuteButton.fell()) {
		const byte *currentDigits = timeDigits(lastSeconds % 86400);
		offsetSeconds = secondsFromTime(currentDigits[0],
		                                currentDigits[1],
		                                currentDigits[2],
		                                (currentDigits[3] + 1) % 10) % 86400;
		reset = true;
	}

	if(brightnessButton.fell()) {
		brightness = (brightness + 1) % 0x10;
		setIntensity(lc, brightness);
	}

	if(firstLoop) {
		brightness = 0;
		firstLoop = false;
		offsetSeconds = 0;
		reset = true;
	}
	
	if(reset) {
		offsetTicks = 0;
		lastTicks = ticks;
		offsetTicks = -ticks;
		lastSeconds = offsetSeconds;
	} else {
		if(lastTicks > ticks) {
			offsetSeconds = lastSeconds;
			offsetTicks = maxTicks - lastTicks;
		}
	}

	for(byte i = 0; i < 4; ++i) {
		buttonArray[i]->update();
	}

	lastSeconds = offsetSeconds + (ticks + offsetTicks) / 1000;
	const byte *currentDigits = timeDigits(lastSeconds % 86400);

	// Write the clock
	if(currentDigits[0] == 0) {
		writeLetter(lc, 3, space);
	} else {
		writeLetter(lc, 3, digits[currentDigits[0]]);
	}
	writeLetter(lc, 2, digits[currentDigits[1]]);
	writeLetter(lc, 1, digits[currentDigits[2]], (lastSeconds % 2) == 1);
	writeLetter(lc, 0, digits[currentDigits[3]]);
}
 

Nima Jooyandeh facts.