Welcome! :-)

This is both a wiki (a website editable by all) and a blog (an online diary about the stuff Alex Schroeder reads and does). If you’re a friend or relative, you might be interested in reading Life instead of this page. If you’ve come here from an RPG blog, you might want to head over to RPG. There are other similar categories to be found on the SiteMap.

Für Rollenspieler gibt es ebenfalls eine eigene RSP Kategorie.

2016-07-25 IP number range to regular expression

I can’t believe how long it took me to develop a little script to do the following: given an IP number, look up the IP number network range this particular number falls in and create a regular expression that matches all of them.


  1. the IP number of a spam wiki edit is
  2. whois says the range is -
  3. the regular expression we want is ^45\.58\.42\.

Harder example:

  1. the IP number of a spam wiki edit is
  2. whois says the range is -
  3. the regular expression we want is ^46\.101\.([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-7])$

It took me way too long to get at the following solution written in Perl 5. Let me know, if you have a more elegant solution. It doesn’t have to be written in Perl.

The script I wrote is called with an IP number or the string “test” to run its tests.


#!/usr/bin/env perl

use Modern::Perl;
use Net::Whois::IP qw(whoisip_query);
use Test::More;

die "Usage: banned-host-regexp (IP-NUMBER|test)\n" unless $ARGV[0];

sub get_range {
  my $ip = shift;
  my $response = whoisip_query($ip);
  my ($start, $end);
  my $ip_regexp = '(?:[0-9]{1,3}\.){3}[0-9]{1,3}';
  for (sort keys(%{$response})) {
    if (($start, $end)
	= $response->{$_} =~ /($ip_regexp) *- *($ip_regexp)/) {
  die "Did not find an IP range in the response:\n"
      . join("\n", map { "$_: $response->{$_}" } sort keys(%{$response}))
      . "\n" unless $start and $end;
  return $start, $end;

sub get_groups {
  my ($from, $to) = @_;
  my @groups;
  if ($from < 10) {
    my $to = $to >= 10 ? 9 : $to;
    push(@groups, [$from, $to]);
    $from = $to + 1;
  while ($from < $to) {
    my $to = int($from/100) < int($to/100) ? $from + 99 - $from % 100 : $to;
    if ($from % 10) {
      push(@groups, [$from, $from + 9 - $from % 10]);
      $from += 10 - $from % 10;
    if (int($from/10) < int($to/10)) {
      if ($to % 10 == 9) {
	push(@groups, [$from, $to]);
	$from = 1 + $to;
      } else {
	push(@groups, [$from, $to - 1 - $to % 10]);
	$from = $to - $to % 10;
    } else {
      push(@groups, [$from - $from % 10, $to]);
    if ($to % 10 != 9) {
      push(@groups, [$from, $to]);
      $from = 1 + $to; # jump from 99 to 100
  return \@groups;

if ($ARGV[0] eq 'test') {
  is_deeply(get_groups('2', '5'), [[2, 5]], "2-5");
  is_deeply(get_groups('9', '15'), [[9, 9], [10, 15]], "9-15");
  # diag explain get_groups('80', '90');
  is_deeply(get_groups('80', '90'), [[80, 89], [90, 90]], "80-90");
  is_deeply(get_groups('85', '99'), [[85, 89], [90, 99]], "85-99");
  # diag explain get_groups('80', '110');
  is_deeply(get_groups('80', '110'), [[80, 99], [100, 109], [110, 110]], "80-110");
  # diag explain get_groups('0', '127');
  is_deeply(get_groups('0', '127'), [[0, 9], [10, 99], [100, 119], [120, 127]], "0-127");
  # diag explain get_groups('0', '255');
  is_deeply(get_groups('0', '255'), [[0, 9], [10, 99], [100, 199], [200, 249], [250, 255]], "0-255");

sub get_regexp_range {
  my @chars;
  for my $group (@{get_groups(@_)}) {
    my ($from, $to) = @$group;
    my $char;
    for (my $i = length($from); $i >= 1; $i--) {
      if (substr($from, - $i, 1) eq substr($to, - $i, 1)) {
	$char .= substr($from, - $i, 1);
      } else {
	$char .= '[' . substr($from, - $i, 1) . '-' . substr($to, - $i, 1). ']';
    push(@chars, $char);
  return join('|', @chars);

if ($ARGV[0] eq 'test') {
  is(get_regexp_range('2', '2'), '2', "2-2");
  is(get_regexp_range('2', '5'), '[2-5]', "2-5");
  is(get_regexp_range('2', '15'), '[2-9]|1[0-5]', "2-15");
  is(get_regexp_range('9', '15'), '9|1[0-5]', "9-15");
  is(get_regexp_range('2', '20'), '[2-9]|1[0-9]|20', "2-20");
  is(get_regexp_range('2', '25'), '[2-9]|1[0-9]|2[0-5]', "2-25");
  is(get_regexp_range('2', '35'), '[2-9]|[1-2][0-9]|3[0-5]', "2-35");
  is(get_regexp_range('80', '99'), '[8-9][0-9]', "80-99");
  is(get_regexp_range('85', '99'), '8[5-9]|9[0-9]', "85-99");
  is(get_regexp_range('80', '110'), '[8-9][0-9]|10[0-9]|110', "80-110");
  is(get_regexp_range('0', '127'), '[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-7]', "0-127");

sub get_regexp_ip {
  my ($from, $to) = @_;
  my @start = split(/\./, $from);
  my @end = split(/\./, $to);
  my $regexp = "^";
  for my $i (0 .. 3) {
    if ($start[$i] eq $end[$i]) {
      $regexp .= $start[$i];
    } elsif ($start[$i] eq '0' and $end[$i] eq '255') {
    } elsif ($start[$i + 1] > 0) {
      $regexp .= '(' . $start[$i] . '\.('
	  . get_regexp_range($start[$i + 1], '255') . ')|'
	  . get_regexp_range($start[$i] + 1, $end[$i + 1]) . ')';
      $regexp .= '\.';
    } else {
      $regexp .= '(' . get_regexp_range($start[$i], $end[$i]) . ')';
      $regexp .= $i < 3 ? '\.' : '$';
    $regexp .= '\.' if $i < 3;
  return $regexp;

if ($ARGV[0] eq 'test') {
  is(get_regexp_ip('', ''),
     ' -');
  is(get_regexp_ip('', ''),
     ' -');

if ($ARGV[0] eq 'test') {
  exit 0;

print get_regexp_ip(get_range($ARGV[0])) . "\n";


Comments on 2016-07-25 IP number range to regular expression

Hi Alex, love your blog.

Why not turn it to an integer and do the math as if it were a subnet? I’m pretty sure the output from whois will always be a subnet range.

If you don’t want to roll your own, http://search.cpan.org/~luismunoz/NetAddr-IP-4.007/IP.pm. The trick is to turn the range form into a CIDR form.

Ben Bennett 2016-07-26 11:12 UTC

Add Comment

2016-07-22 New Server

Welcome to the new server! A year later and I’ve finally made the move to Debian Jessie.

My notes: Sibirocobombus.


Add Comment

2016-07-18 w3m and Emacs Wiki

w3m is a text browser. I use it a lot if I need to test stuff. Recently, I got an email saying that Emacs Wiki had an invalid certificate. Everything seemed to be fine, however. But a few minutes ago, I tried to run w3m https://www.emacswiki.org/ from my Terminal (OSX El Capitan) and got an SSL error.

alex@Megabombus:~$ w3m -dump_extra https://www.emacswiki.org/
SSL error: error:00000000:lib(0):func(0):reason(0)
w3m: Can't load https://www.emacswiki.org/.

Thanks to twb on #emacs, I found the problem. He said: “What’s ldd called on OS X? Find out if w3m is using gnutls or openssl.” The equivalent to ldd on OSX is otool -L.

As it turns out, a simple reinstall switched from libssl 0.9.8 to 1.0.0 and now it works.

alex@Megabombus:~$ otool -L `which w3m`
	/usr/local/lib/libgc.1.dylib (compatibility version 2.0.0, current version 2.3.0)
	/usr/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 50.0.0)
	/usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 50.0.0)
	/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
alex@Megabombus:~$ brew reinstall w3m
==> Reinstalling w3m
==> Downloading https://homebrew.bintray.com/bottles/w3m-0.5.3.el_capitan.bottle.tar.gz
######################################################################## 100,0%
==> Pouring w3m-0.5.3.el_capitan.bottle.tar.gz
🍺  /usr/local/Cellar/w3m/0.5.3: 23 files, 1.7M
alex@Megabombus:~$ otool -L `which w3m`
	/usr/local/opt/bdw-gc/lib/libgc.1.dylib (compatibility version 2.0.0, current version 2.3.0)
	/usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)


Add Comment

2016-07-18 Bretagne

We spent a week in Roscoff, Bretagne. It was the week of Bastille day, of the arrival of the Tour de France à la voile, with the main season just about to start. The weather was beautiful, the water was cold, the food was delicious and Claudia’s insomnia was gone. That surprised both of us. The very first day we were back in Switzerland, it raised it’s ugly head again: one and a half hours of waiting for sleep from Saturday to Sunday, then no sleep until five in the morning from Sunday to Monday. We’re about ready to call for dowsing rods and dowsing pendulums. Grrr!

Here are some pictures of our trip:


Add Comment

2016-07-08 Saas Fee

A few days ago we were in the Wallis. We visited Saas Fee:

In the local museum, I saw this old map: Vallesia superior, ac inferior, Wallis, le Valais, by Gabriel Walser from 1768. You can download a 6208×5151 pixel image.


Add Comment

2016-07-08 Rheinfall

A while ago we visited the Rhine Falls.


Comments on 2016-07-08 Rheinfall

Wow, Schwarzweiss, gut! Ich versuche es immer wieder, hatte bisher aber keinen Erfolg.

Chris 2016-07-09 12:52 UTC

Hah, ist leider etwas billig: das ist ein Filter der Kamera, inklusive dramatischem Kontrast und Rahmen. :)

– AlexSchroeder 2016-07-09 15:36 UTC

Das ist doch super; mir ist alles lieb, was mir hilft bessere Bilder zu machen. Ich brauche Lightroom, das hat auch Schwarzweissfilter, aber irgendwie…

Chris 2016-07-10 10:53 UTC

Du hast recht. Auf dem iPhone hatte ich eine Weile ein Photo App namens Vint B&W MII, die Bilder nach einem eigenen Algorithmus entfärbte und bessere Resultate lieferte als ein einfaches Entfärben im Gimp oder iPhoto. Offensichtlich gibt es da eine Kunst zum Thema. Aber worauf man schauen muss, weiss ich leider auch nicht. Früher hatte schwarz-weiss Film ja eine Affinität für rot.

Jetzt habe ich mir gerade die Tutorial-Seite für schwarze-weiss Bilder mit Gimp angeschaut. Da gibt es wirklich viel zum Ausprobieren.

– AlexSchroeder 2016-07-10 18:37 UTC

Add Comment

2016-07-08 Perlbrew With Threads

I want to run fgallery on my laptop running OSX instead of uploading the images and running fgallery on the server in order to generate the album. Perhaps that’s stupid. Anyway, the first order of business is to compile a version of Perl that has threads.

perlbrew install-multiple --both thread perl-5.22.0

Let’s see…

alex@Megabombus:~/src/fgallery-1.8.2$ perlbrew exec --with perl-5.22.0-thread-multi ./fgallery
Can't locate Image/ExifTool.pm in @INC (you may need to install the Image::ExifTool module) (@INC contains: /Users/alex/perl5/perlbrew/perls/perl-5.22.0-thread-multi/lib/site_perl/5.22.0/darwin-thread-multi-2level /Users/alex/perl5/perlbrew/perls/perl-5.22.0-thread-multi/lib/site_perl/5.22.0 /Users/alex/perl5/perlbrew/perls/perl-5.22.0-thread-multi/lib/5.22.0/darwin-thread-multi-2level /Users/alex/perl5/perlbrew/perls/perl-5.22.0-thread-multi/lib/5.22.0 .) at ./fgallery line 27.

OK, so now for dependencies, which have to be reinstalled.

alex@Megabombus:~/src/fgallery-1.8.2$ perlbrew use perl-5.22.0-thread-multi
alex@Megabombus:~/src/fgallery-1.8.2$ cpanm Image::ExifTool
--> Working on Image::ExifTool
Fetching http://www.cpan.org/authors/id/E/EX/EXIFTOOL/Image-ExifTool-10.20.tar.gz ... OK
Configuring Image-ExifTool-10.20 ... OK
Building and testing Image-ExifTool-10.20 ... OK
Successfully installed Image-ExifTool-10.20
1 distribution installed

I also got myself a copy of the facedetect. When I tried it:

alex@Megabombus:~/src/facedetect-master$ ./facedetect 
Traceback (most recent call last):
  File "./facedetect", line 8, in <module>
    import numpy as np
ImportError: No module named numpy

OK, the page mentions opencv and apparently Homebrew offers something like it.

alex@Megabombus:~/src/fgallery-1.8.2$ brew install homebrew/science/opencv
alex@Megabombus:~/src/fgallery-1.8.2$ ../facedetect-master/facedetect 
usage: facedetect [-h] [--biggest] [--best] [-c] [--data-dir DIRECTORY] [-q]
                  [-s FILE] [--search-threshold PERCENT] [-o OUTPUT] [-d]
facedetect: error: too few arguments

I guess it works!

OK, so back to my “default” Perl and let’s check that command line.

alex@Megabombus:/Users/Shared/Fotos 2016$ perlbrew exec --with perl-5.22.0-thread-multi ~/src/fgallery-1.8.2/fgallery -f Saas\ Fee/Album/ 2016-saas-fee
error: cannot execute exiftran or exifautotran for lossless JPEG autorotation

OK, so more problems. Let’s install fbida.

alex@Megabombus:/Users/Shared/Fotos 2016$ brew install fbida
alex@Megabombus:/Users/Shared/Fotos 2016$ perlbrew exec --with perl-5.22.0-thread-multi ~/src/fgallery-1.8.2/fgallery -f Saas\ Fee/Album/ 2016-saas-fee
reading completed        
facedetect: error: cannot load HAAR_FRONTALFACE_ALT2 from /usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml
error: command "facedetect --best --center 2016-saas-fee/imgs/P7020100.jpg" failed

OK, some data is missing. I downloaded a recent copy of the version 2 zip file and extracted just the data:

alex@Megabombus:~/src$ unzip /Users/alex/Downloads/opencv-2.4.13.zip "opencv-2.4.13/data*"

And then I tried again:

alex@Megabombus:/Users/Shared/Fotos 2016$ facedetect --data-dir /Users/alex/src/opencv-2.4.13/data/ --best --center 2016-saas-fee/imgs/P7020100.jpg

And it worked! (No output because it contains no face, I presume.)

So how can we pass this option from fgallery to facedetect? I think the simplest solution is to just change the DATA_DIR setting in facedetect:

alex@Megabombus:~$ diff ~/src/facedetect-master/facedetect bin/facedetect 
< DATA_DIR = '/usr/share/opencv/'
> DATA_DIR = '/Users/alex/src/opencv-2.4.13/data/'

Oh, and I dedided to install the optional stuff as well.

alex@Megabombus:/Users/Shared/Fotos 2016$ brew install jpegoptim
alex@Megabombus:/Users/Shared/Fotos 2016$ brew install pngcrush
alex@Megabombus:/Users/Shared/Fotos 2016$ brew install p7zip
alex@Megabombus:/Users/Shared/Fotos 2016$ perlbrew exec --with perl-5.22.0-thread-multi cpanm Cpanel::JSON::XS

And now, finally, the test:

alex@Megabombus:/Users/Shared/Fotos 2016$ perlbrew exec --with perl-5.22.0-thread-multi ~/src/fgallery-1.8.2/fgallery -f Saas\ Fee/Album/ 2016-saas-fee
reading completed        
processing completed        
generating archive...

Yay! Hours wasted but mission accomplished! :)

Copy it over:

alex@Megabombus:/Users/Shared/Fotos 2016$ scp -r 2016-saas-fee/ alexschroeder.ch:alexschroeder.ch/gallery/

As it turns out, I dislike the stupid exif comments my camera leaves and sometimes I create little text files with alternate captions. Thus, I need the -c txt option. And then to copy it all over to the gallery.

alex@Megabombus:/Users/Shared/Fotos 2016$ perlbrew exec --with perl-5.22.0-thread-multi ~/src/fgallery-1.8.2/fgallery -c txt -f Saas\ Fee/Album/ 2016-saas-fee
alex@Megabombus:/Users/Shared/Fotos 2016$ rsync --progress --recursive 2016-saas-fee alexschroeder.ch:alexschroeder.ch/gallery/


Add Comment

2016-07-07 Unprofessional

An excellent article by Zak, Stay Unprofessional. I laughed, when I came to the following paragraph on alienation.

In short: it’s possible for even indie RPG authors to be so professional they aren’t making anything they want to (or have to) live with. In a curious quirk of early 21st century post-hobby production, they have alienated themselves from their labor, with almost no help from the larger capitalist system.

How true.

It’s why I’m only doing the stuff I want for my own games. The amount of effort I put into the Caverns of Slime for Fight On! #15 which never got published showed me that every step of the process has to be enjoyable. If it is not, if it is premised on some later reward and recognition, what happens when the reward does not manifest? Time wasted, that’s what.


Add Comment

2016-07-07 Preparing a Sandbox

Recently, Rob Monroe asked about tools to use when preparing a sandbox.

I listed the following:

  1. A map. I started my last one with a random Text Mapper map using the Hex Based Campaign Design blog posts by the Welsh Piper.[1][2]
  2. I also enjoy +Kevin Crawford’s system of assigning two tags to each hex as described in his book Red Tide, and of establishing a cabinet of evil movers and shakers right at the beginning as described in his book An Echo Resounding.

Alternatively, I could also imagine running my next game as a point crawl based on the structured input of my players as suggested by +Jason Lutes in The Perilous Wilds.

Jason Lutes then linked to two examples of how he did it in 2013, which are also based on blog posts by the Welsh Piper, random generators by Chaotic Shiny, and all that, in order to create the world and a village.

It looks like a lot of work but it looks super beautiful!

I still think that starting out with a random Text Mapper Map and adding notes and settlements to the text describing the area and using the tool to render a picture of the area still is the best value for my time. See How To Get Started With Text Mapper for more.

Tags: s

Add Comment

2016-07-07 Being a RPG Referee

This post is a translation of an old post I wrote back in 2014 about the responsibilities of a GM—or referee, as I like to call myself in English. As I translate from German to English, I’ll note that sentences are longer and more convoluted than usual. Welcome to the German Way. :)

Sometimes I meet people in my games that would like to run a campaign. Sometimes, they just hope that the current group will just stay together and keep playing, at the same location, at the same time. If you’re already playing with friends looking for a new referee, then that’s a great way to start. But sometimes just wanting to run a game is not good enough…

  • Referees must find new players on Facebook, Google+, Nearby Gamers, forums, friends, partners, relatives, coworkers, and so on. Yes, everybody should share in this responsibility, but in my experience, the referee is usually the one with the biggest network.
  • Referees must coordinate schedules. Somebody has to announce game dates. When game day is moved around a lot, then players like me, people with no spontaneous personalities and inflexible tendencies, will have a hard time dealing with this. I can’t come on short notice, and when game day is canceled I’m angry because I would have preferred doing something else instead. Sooner or later, game day will get a lower priority and it won’t be belong before I’ll be making other plans just to be sure that I’ve got something going on for sure.
  • Referees must solve conflicts. Players trash talking gays, prostitutes, foreigners? Anybody can speak up, but when I’m a guest somewhere else, I might feel that I’d rather send a friendly good bye email rather than engage in the conflict and play with such people. As a referee, we’re not only responsible for finding new players, we’re also responsible for maintaining a pleasant gaming culture at the table.
  • Referees must pick or propose rules they like. There’s no point in picking a game the referee doesn’t like to run.
  • Referees must explain rules, clarify them, establish rulings, remind players of the rules. Sure, you can delegate the rules to a player, but in general it won’t work all that well. It’s a tricky thing to contradict the referee, breaking the flow, asking for short retcons, i.e. the alteration of previously established facts in the continuity of the events at the table.
  • Referees determine spot light time and need to intervene. Some players are louder than others, have more ideas than others, constantly convince their fellow players to change their minds. Sure, the distribution of introverts and extroverts is a natural thing and often the behavior matches what people enjoy most. And yet, this must not get out of hand. A clear turn order, allowing quiet players to make particular decisions without interference from their fellow players and similar small interventions will help balance spot light.
  • Referees are responsible for the timing. When do we start, when do we stop, do we begin in media res, do we end with a cliff hanger, do we have an extra half hour, should we aim for a close? As a referee, one of your eyes is always watching the clock.
  • Referees are responsible for the tempo. Is the game boring and we need to push on, or do we chill and take in the scene, listen to long speeches and enjoy the little details? Referees react when players pick up their phones, yawn, start doodling, sort dice, or stack dice.
  • Referees run the game. When they’re unsure, the table loses focus. When they are sure of themselves, players can focus on the game.
  • Referees usually host the game. This is maybe a European thing where space is expensive and thus game shops are small and offer no organized play or open tables, and pubs and coffee shops are crowded. We meet at home. We invite strangers to play in our living rooms. And that’s why referees usually provide the books, the binders, the material, the extra dice, pens, erasers, mats, water jars, glasses, bowls for chips and dried fruit, pistachios, peanuts, tangerines, or dishes for trash.
  • Referees must make time to prepare. As a player, you can afford to just show up for game night and not know a thing. As a referee, you will have to think about the game, take notes, have dreams…
  • Referees must write session reports. Players don’t often do that. It’s great when they do, don’t get me wrong. But it’s hard to force them if they don’t, and you need notes to create continuity. Somebody has to go through the last session and think about what happened. Players won’t remember the details. They won’t embellish events. As a player, I’ll just scribble a few notes on the margins of my character sheet. I’m not going to write a session report.
  • Referees must create a setting. It can be small, a dungeon, a village, a wilderness, a city, multiple cities, a whole surface world, a network of underworlds, a beyond, the planes, everything needs thought, setting books must be read if you decide to use a published setting.
  • Referees must prepare adventures. Or they need to prepare situations that can lead to conflict. And as we saw with this list, this is not the only thing they need to do!

I don’t want to scare anybody. Being a referee is a great hobby. I just want to prevent people from thinking that all it takes is to read an adventure and show up somewhere in order to run a game. There are many responsibilities which end up being ours.

At the time, there was an interesting discussion on G+ where people talked about running a game as a service and delegating tasks. My first reaction to all these responses was always the thought: “I wish!” Yes, I do. But I wrote this list remembering friends and acquaintances, convention games and statements from fellow players, people wanting to take over one of my groups, asking whether I know any players looking for a game, complaining about players not mapping or not writing session reports, or not reading the session reports they wrote, and I think to myself: “Stop dreaming! Sure, that would be nice but primarily, this is your job.”

And yes, there are game systems that do without some of these responsibilities. Some games require no preparation, some games require no explicit referee, but most of the responsibilities I listed remain. You don’t need to prep a plot for Mountain Witch but somebody still has to know the rules. You don’t need to have a referee for Western City but you still need a host. You don’t need to learn about a setting in Dungeon World as you can create a setting together when the game starts, but everything else still holds true.


Add Comment



Please make sure you contribute only your own work, or work licensed under the GNU Free Documentation License. See Info for text formatting rules. You can edit this page if you need to fix typos. You can subscribe to updates by email without leaving a comment.

To save this page you must answer this question:

Please say HELLO.

Referrers: Planet Emacsen The City of Iron: Dolmenwood: Thinking About Random Encounter Tables Diary Gothridge Manor