Diary

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-12-05 Advent of Code in Perl 5

Today I don’t have a lot of time for Advent of Code. So, Perl 5 it is. My Swiss Army knife!

I’ll have to save the interesting languages for days when I have a lot of time. This will be hard. :(

Question 1: You start with a string such as “abc” and append an index, then compute the MD5 hash, and if its hex representation starts with five zeroes, then the sixth character is a letter in your password. Keep increasing the index until you have eight letters for your password. Using “abc” as the example, the first match would be “abc3231929” which produces a hash of “00000155f8105dff7f56ee10fa9b9abd” and thus the first letter of the password would be “1”.

use Modern::Perl;
use Digest::MD5 qw(md5_hex);

my $prefix = 'abc';
my $i = 0;
my $length = 0;
my $pwd;
while ($length < 8) {
  my $digest = md5_hex($prefix . $i++);
  if ('00000' eq substr($digest, 0, 5)) {
    $pwd .= substr($digest, 5, 1);
    $length++;
    print "$pwd\n";
  }
}

Question 2: Same as before, but now the the sixth character indicates the position of the seventh character. In the example above, this means that position 1 has the number “5”. The password is zero indexed. Ignore positions outside the password and don’t overwrite characters you’ve alreay found.

use Modern::Perl;
use Digest::MD5 qw(md5_hex);

$" = '';
my $prefix = 'abc';
my $i = 0;
my $length = 0;
my @pwd = qw(_ _ _ _ _ _ _ _);
while ($length < 8) {
  my $digest = md5_hex($prefix . $i++);
  if ('00000' eq substr($digest, 0, 5)) {
    my $p = hex(substr($digest, 5, 1));
    next if $p >= 8 or $pwd[$p] ne "_";
    my $c = substr($digest, 6, 1);
    $length++;
    $pwd[$p] = $c;
    print "@pwd\n";
  }
}

Tags:

Add Comment

2016-12-04 Advent of Code in Java

I use Java to earn money, even though I don’t really like working with it. Everything is so damn verbose. If you return to writing Java code within Emacs you’ll realize that a big chunk of the code you’re writing gets written by the IDE, e.g. imports. Static typing and object orientation has resulted in basically every object defining a separate interface which you need to memorize. It’s hard to write code without completion. Anyway, I decided to solve today’s Advent of Code riddles using Java.

First question. Your input is a list of strings. Each string has the form NAME-ID[CHECKSUM]. NAME consists of lowercase letters separated by dashes, ID is a number and CHECKSUM are the five most common letters in NAME, sorted by frequency with ties broken by alphabetical sorting. What is the sum of all the IDs where the CHECKSUM is correct?

This checksum is valid, for example and thus the sum would be 123:

zzzyyyxxxwwva-123[xyzwa]

The enjoyable part about this exercise was that I got to use Java 8 streams. Yay me!

import java.lang.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.*;

class Part1 {

  Scanner scanner = new Scanner(System.in);
  Pattern p = Pattern.compile("([a-z-]+)-([0-9]+)\\[([a-z]{5})\\]");

  String readRoom() {
    return scanner.next();
  }

  int readValidSectorId () {
    String room = readRoom();
    Matcher m = p.matcher(room);
    if (m.matches()) {
      String name = m.group(1);
      int id = Integer.parseInt(m.group(2));
      String checksum = m.group(3);
      // count the occurences of every character
      HashMap<Character, Integer> charMap = new HashMap<Character, Integer>();
      for (Character c: name.toCharArray()) {
        if (charMap.containsKey(c)) {
          charMap.put(c, charMap.get(c) + 1);
        } else {
          charMap.put(c, 1);
        }
      }
      // get the five most common characters
      String computed =
        charMap.keySet().stream()
        .filter((elem) -> elem.charValue() != '-')
        .sorted(new Comparator<Character>() {
            @Override
            public int compare(Character a, Character b) {
              int r = charMap.get(b).compareTo(charMap.get(a));
              if (r == 0) {
                return a.compareTo(b);
              }
              return r;
            }
          })
        .limit(5)
        .map(Object::toString)
        .collect(Collectors.joining());
      if (checksum.equals(computed)) {
        return id;
      }
      // System.out.println(computed + " != " + checksum);
    }
    return 0;
  }

  int sum () {
    int sum = 0;
    try {
      while (true) {
        sum += readValidSectorId();
      }
    } catch (NoSuchElementException e) {
      // end of file
    }
    return sum;
  }

  public static void main(String args[]) {
    Part1 p = new Part1();
    System.out.println("Sum: " + p.sum());
  }
}

Second question. For every string with correct CHECKSUM, rotate the letters in NAME by the ID. The example given shows how the letter q in a NAME with ID 343 would end up a v. To find the actual answer asked for in the question, grep the result for a line containing “north”.

import java.lang.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.*;

class Part2 {

  Scanner scanner = new Scanner(System.in);
  Pattern p = Pattern.compile("([a-z-]+)-([0-9]+)\\[([a-z]{5})\\]");

  String readRoom() {
    return scanner.next();
  }

  void readValidSectorId () {
    String room = readRoom();
    Matcher m = p.matcher(room);
    if (m.matches()) {
      String name = m.group(1);
      int id = Integer.parseInt(m.group(2));
      String checksum = m.group(3);
      // count the occurences of every character
      HashMap<Character, Integer> charMap = new HashMap<Character, Integer>();
      for (Character c: name.toCharArray()) {
        if (charMap.containsKey(c)) {
          charMap.put(c, charMap.get(c) + 1);
        } else {
          charMap.put(c, 1);
        }
      }
      // get the five most common characters
      String computed =
        charMap.keySet().stream()
        .filter((elem) -> elem.charValue() != '-')
        .sorted(new Comparator<Character>() {
            @Override
            public int compare(Character a, Character b) {
              int r = charMap.get(b).compareTo(charMap.get(a));
              if (r == 0) {
                return a.compareTo(b);
              }
              return r;
            }
          })
        .limit(5)
        .map(Object::toString)
        .collect(Collectors.joining());
      if (checksum.equals(computed)) {
        // do the rotating
        String s = name.chars()
          .map(c -> {
              if (c == '-') {
                return ' ';
              } else {
                return ((c + id - 'a') % 26 + 'a');
              }
            })
          .mapToObj(c -> Character.toString((char)c))
          .collect(Collectors.joining());
        System.out.println(room + " = " + s);
      }
    }
  }

  void read () {
    try {
      while (true) {
        readValidSectorId();
      }
    } catch (NoSuchElementException e) {
      // end of file
    }
  }

  public static void main(String args[]) {
    Part2 p = new Part2();
    p.read();
  }
}

Tags:

Add Comment

2016-12-03 Donations

Time to start another of these pages. I start these about once a year and keep updating it.

2015 was not a very generously year…

I think simply paying for journalism these days should also count as a donation. After all, I could have read it for free, online.

Today I followed yet another Guardian link and saw a “support us” plea. Ok, I thought. I had already once seen such a link and registered and saw that I needed to enter an annual obligation, which I did not do. But today I saw a one-off PayPal link. That works for me.

Talking about newspapers: for years, I’ve been paying double the subscription fee in order to support the local leftist weekly newspaper [1] and the German translation of Le Monde Diplomatique [2].

  • ProWOZ (CHF 530, half of which is a “real donation”)

Right now, it’s the Wikipedia fundraising campaign. OK, why not. I might not like their politics, and the deletionism, but in general, I must say: I use Wikipedia every single day of my life. It’s the most awesome thing on the net. It’s hard to imagine a time without it.

And I regularly donate about $10/month to the EFF and the FSF, and €120/year to the FSFE.

Tags:

Tags:

Add Comment

2016-12-03 Advent of Code in C

Since the previous two days of Advent of Code went so well, I decided to up the ante and try different programming languages. Today: C. I haven’t written C since abandoning German Atlantis.

The question is this: given three numbers per line, count the potential triangles. “In a valid triangle, the sum of any two sides must be larger than the remaining side.”

Here goes. Save as day3.c, compile using make day3, test from the shell using echo 1 2 3 | ./day3 (not a triangle), save your input in a file called data and run using ./day3 < data.

#include <stdio.h>
#include <stdlib.h>

int read_num () {
  int k;
  if (scanf("%d", &k) == 1) {
    return k;
  }
  return 0;
}

int * read_triangle() {
  static int t[3];
  for (int i = 0; i < 3; i++) {
    int n = read_num();
    if (n == 0) {
      if (i == 0) {
	return 0;
      }
      fprintf(stderr, "triangle incomplete, missing %d sides.\n", 3 - i);
      abort();
    }
    t[i] = n;
  }
  return t;
}

int legal(int *t) {
  if (t[0] + t[1] > t[2]
      && t[0] + t[2] > t[1]
      && t[1] + t[2] > t[0]) {
    return 1;
  }
  return 0;
}

int main() {
  int n = 0;
  int *t;
  while ((t = read_triangle()) != 0) {
    /* if (legal(t) == 1) { */
    /*   printf("triangle: [%d %d %d]\n", t[0], t[1], t[2]); */
    /* } else { */
    /*   printf("not a triangle: [%d %d %d]\n", t[0], t[1], t[2]); */
    /* } */
    n += legal(t);
  }
  printf("Counted %d triangles.\n", n);
  return 0;
}

The setup I had was particularly fortuitous for the second part because the array could just as well hold three triangles instead of just one, and the legal function could return a count instead of just 1 or 0.

In part two, we count vertical triangle candidates. In other words, we read three rows and get three potential triangles. Here’s the example given:

101 301 501
102 302 502
103 303 503
201 401 601
202 402 602
203 403 603

This counts as 6 triangles, because every [101 102 103] is a triangle, [201 202 203] is a triangle, etc.

#include <stdio.h>
#include <stdlib.h>

int read_num () {
  int k;
  if (scanf("%d", &k) == 1) {
    return k;
  }
  return 0;
}

int * read_three_triangles() {
  static int t[9];
  for (int i = 0; i < 9; i++) {
    int n = read_num();
    if (n == 0) {
      if (i == 0) {
	return 0;
      }
      fprintf(stderr, "triangle incomplete, missing %d sides.\n", 3 - i);
      abort();
    }
    t[i] = n;
  }
  return t;
}

int legal(int *t) {
  int n = 0;
  for (int i = 0; i < 3; i++) {
    if (t[i + 0] + t[i + 3] > t[i + 6]
	&& t[i + 0] + t[i + 6] > t[i + 3]
	&& t[i + 3] + t[i + 6] > t[i + 0]) {
      n++;
    }
  }
  return n;
}

int main() {
  int n = 0;
  int *t;
  while ((t = read_three_triangles()) != 0) {
    n += legal(t);
  }
  printf("Counted %d triangles.\n", n);
  return 0;
}

All in all I must say C was quite easy to slide back into. The core itself is easy. Luckily this exercise did not require memory management.

Tags:

Add Comment

2016-12-02 Emacs Avent of Code Day 2

First question of day 2: You start on key 5 on a keypad as shown below. With instructions to go up, down, left or right. Ignore instructions that would take you off the keypad.

1 2 3
4 5 6
7 8 9

The example input provided for the four numbers is shown below. At the end of each line you’ll end up on the key which is part of the answer. In this example the correct answer is 1985.

ULL
RRDDD
LURDL
UUUUD

Code:

(let ((pos 5)
      (code nil)
      (instructions '((U L L)
		      (R R D D D)
		      (L U R D L)
		      (U U U U D))))
  (dolist (instruction instructions)
    (dolist (move instruction)
      (case move
	((U) (when (> pos 3) (setq pos (- pos 3))))
	((D) (when (< pos 7) (setq pos (+ pos 3))))
	((L) (when (not (= (mod pos 3) 1)) (setq pos (- pos 1))))
	((R) (when (> (mod pos 3) 0) (setq pos (+ pos 1))))))
    (push pos code))
  (reverse code))

For the second star, I was too lazy to figure out how to compute the next position and decided to implement tables instead:

    1
  2 3 4
5 6 7 8 9
  A B C
    D

Given a direction, I just list all the possible positions and the results. It’s crude, but it works.

(let ((pos 5)
      (code nil)
      (instructions '((U L L)
		      (R R D D D)
		      (L U R D L)
		      (U U U U D))))
(dolist (instruction instructions)
    (dolist (move instruction)
      (setq pos (funcall move pos)))
    (push pos code))
  (reverse code))

(defun U (pos)
  (case pos
    ((3) 1)
    ((6) 2)
    ((7) 3)
    ((8) 4)
    ((A) 6)
    ((B) 7)
    ((C) 8)
    ((D) 'B)
    (t pos)))

(defun D (pos)
  (case pos
    ((1) 3)
    ((2) 6)
    ((3) 7)
    ((4) 8)
    ((6) 'A)
    ((7) 'B)
    ((8) 'C)
    ((B) 'D)
    (t pos)))

(defun L (pos)
  (case pos
    ((6) 5)
    ((3) 2)
    ((7) 6)
    ((B) 'A)
    ((4) 3)
    ((8) 7)
    ((C) 'B)
    ((9) 8)
    (t pos)))

(defun R (pos)
  (case pos
    ((5) 6)
    ((2) 3)
    ((6) 7)
    ((A) 'B)
    ((3) 4)
    ((7) 8)
    ((B) 'C)
    ((8) 9)
    (t pos)))

Tags:

Add Comment

2016-12-01 Emacs Advent of Code

I looked at Advent of Code and foolishly decided to solve the first riddle. Perhaps we’ll learn something about Elisp coding style? Feel free to leave comments regarding the code.

You must follow instructions: R or L means turn left or right, followed by a number of steps to take. What’s the final distance in steps? Example: “R5, L5, R5, R3 leaves you 12 blocks away.”

(let ((instructions (mapcar (lambda (s)
			      (cons (aref s 0)
				    (string-to-number (substring s 1))))
			    (split-string "R5, L5, R5, R3")))
      (direction 0)
      (x 0)
      (y 0))
  (dolist (instruction instructions)
    (destructuring-bind (turn . steps) instruction
      (setq direction (mod (funcall (if (= turn ?R) '1+ '1-) direction) 4))
      (case direction
	(0 (setq y (+ y steps)))
	(1 (setq x (+ x steps)))
	(2 (setq y (- y steps)))
	(3 (setq x (- x steps))))))
  (+ (abs x) (abs y)))

Sadly, for the second star, my solution fails… The question is now: which location was visited twice? The example provided says “if your instructions are R8, R4, R4, R8, the first location you visit twice is 4 blocks away, due East.” The code works for the example given but fails for the data I was provided with in the test.

(let ((instructions (mapcar (lambda (s)
			      (cons (aref s 0)
				    (string-to-number (substring s 1))))
			    (split-string "R8, R4, R4, R8")))
      (direction 0)
      (x 0)
      (y 0)
      (last-x 0)
      (last-y 0)
      (seen nil)
      (final-x nil)
      (final-y nil))
  (or (catch 'twice
	(dolist (instruction instructions)
	  (destructuring-bind (turn . steps) instruction
	    (setq direction (mod (funcall (if (= turn ?R) '1+ '1-) direction) 4))
	    (case direction
	      (0 (setq y (+ y steps)))
	      (1 (setq x (+ x steps)))
	      (2 (setq y (- y steps)))
	      (3 (setq x (- x steps)))))
	  (dolist (line (cdr seen))	; skip the last element
	    (destructuring-bind ((x1 . y1) (x2 . y2)) line
	      (when (or (and (= x last-x) ; last move was vertical 
			     (= y1 y2)	  ; looking at a horizontal
			     (>= x x1)  (<= x x2)
			     (or (and (<= y y1)  (>= last-y y1))
				 (and (>= y y1)  (<= last-y y1)))
			     (setq final-x x
				   final-y y1))
			(and (= y last-y) ; last move was horizontal
			     (= x1 x2)	  ; looking at a vertical
			     (>= y y1)  (<= y y2)
			     (or (and (<= x x1)  (>= last-x x1))
				 (and (>= x x1)  (<= last-x x1)))
			     (setq final-x x1
				   final-y y)))
		;; (error "Moving from %S to %S crosses the line from %S to %S in %S"
		;; 	   (cons last-x last-y) (cons x y)
		;; 	   (cons x1 y1) (cons x2 y2)
		;; 	   seen)
		(throw 'twice (+ (abs final-x) (abs final-y))))))
	  (push (list (cons last-x last-y) (cons x y)) seen)
	  (setq last-x x
		last-y y)))
      (+ (abs x) (abs y))))

Well, I decided to brute force it and remember every point I moved through because my “crossing lines” solution was obviously off by 3. Perhaps I just reported the wrong result, who knows.

(let ((instructions (mapcar (lambda (s)
			      (cons (aref s 0)
				    (string-to-number (substring s 1))))
			    (split-string "R8, R4, R4, R8")))
      (direction 0)
      (x 0)
      (y 0)
      (last-x 0)
      (last-y 0)
      (seen '((0 . 0))))
  (or (catch 'twice
	(dolist (instruction instructions)
	  (destructuring-bind (turn . steps) instruction
	    (setq direction (mod (funcall (if (= turn ?R) '1+ '1-) direction) 4))
	    (case direction
	      (0 (setq y (+ y steps)))
	      (1 (setq x (+ x steps)))
	      (2 (setq y (- y steps)))
	      (3 (setq x (- x steps)))))
	  (while (not (and (= last-x x) (= last-y y)))
	    (if (not (= last-x x))
		(setq last-x (funcall (if (< last-x x) '1+ '1-) last-x))
	      (setq last-y (funcall (if (< last-y y) '1+ '1-) last-y)))
	    (let ((last (cons last-x last-y)))
	      (if (member last seen)
		  (error "Repetition seen: %S in %S" last seen)
		(push last seen))))))
      (error "No repetitions in %S" seen)))

Tags:

Add Comment

2016-11-27 Work and Basic Income

We lost the Basic Income initiative in Switzerland but I think I need to start another link collection page.

Fuck work. “Economists believe in full employment. Americans think that work builds character. But what if jobs aren’t working anymore?”

Tags:

Add Comment

2016-11-26 Mutt

I have some mail accounts for my machines that receive mail from the Internet, but almost all of it is spam. When I tried to forward it to my Gmail account, Google started rejecting it, because it was so much spam. I have since installed Spam Assassin, but I still want to filter the stuff locally. And I’m running into usability issues with mail. Apparently I can only delete messages based on their number and I haven’t seen a good way to navigate the mess.

A while ago I installed mutt to give it a try, and now it’s several weeks later and I don’t remember what I did. This blog post is going to be the note to myself I wish I had written the first time around.

  • / – search (e.g. for “root”)
  • D – delete messages by pattern (e.g. for “.” when you’re done and ready to delete all the spam)

Or:

  • T – tag messages by pattern
  • ; – tag prefix
  • d – delete message

Good patterns:

  • parcel|shipment|invoice|statement|suspended|support|receipt|document|scan|order|attention|generika|insurance|china|suspicious|

Tags:

Add Comment

2016-11-26 GD again

I wanted to use GD::Barcode::QRcode but it would not work. Was GD not installed? I tried to install it using cpanm GD but it wouldn’t work. Tests kept failing.

I had something installed via Homebrew. OK, so brew unlink gd and see whether that helps. Now try installing cpanm GD and I get a different problem. Apparently my libgd is borked. I see unmet recommended dependencies when looking at brew info libgd. One strikes me as particularly odd: no libpng installed. Trying brew reinstall libgd now. Regenerating the font cache seems to take a while.

I am already regretting this.

And indeed, it doesn’t help. cpanm GD still fails.

alex@Megabombus:~/.cpanm/work/1480171923.14458/GD-2.56$ prove -v -b -l t/GD.t 
t/GD.t .. 
1..11
ok 1 - use GD;
ok 2 - use GD::Simple;
# Testing using gd2 support.
ok 3 - image comparison test 1
ok 4 - image comparison test 2
ok 5 - image comparison test 3
ok 6 - image comparison test 4
ok 7 - image comparison test 5
ok 8 - image comparison test 6
not ok 9 - image comparison test 7
ok 10 - round trip gd
ok 11 - round trip gd2

#   Failed test 'image comparison test 7'
#   at t/GD.t line 249.
# Looks like you failed 1 test of 11.
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/11 subtests 

Test Summary Report
-------------------
t/GD.t (Wstat: 256 Tests: 11 Failed: 1)
  Failed test:  9
  Non-zero exit status: 1
Files=1, Tests=11,  1 wallclock secs ( 0.04 usr  0.01 sys +  0.16 cusr  0.03 csys =  0.24 CPU)
Result: FAIL

Looking at the test code, it seems that the essential bits are related to fonts:

  # Some TTFs
  $im->stringFT($black,FONT,12.0,0.0,20,20,"Hello world!") || warn $@;
  $im->stringFT($red,FONT,14.0,0.0,20,80,"Hello world!") || warn $@;
  $im->stringFT($blue,FONT,30.0,-0.5,60,100,"Goodbye cruel world!") || warn $@;

Time to cpanm -f GD

Tags:

Add Comment

2016-11-17 Monit

Every now and then, the Monit certificate will expire. Notes to myself:

cd /etc/ssl/localcerts
sudo openssl req -new -x509 -days 365 -nodes -config monit.cnf -out monit.pem -keyout monit.pem
sudo chown root.root monit.pem
sudo chmod 0700 monit.pem
sudo service monit restart
sudo openssl x509 -noout -in monit.pem -fingerprint -sha1

Remember the fingerprint.

Getting Chrome to accept self-signed localhost certificate: visit the offending site, ignoring the security warning.

  1. On the site you want to add, right-click the red lock icon in the address bar:
  2. Click the tab labeled Connection, then click Certificate Information
  3. Click the Details tab, the click the button Copy to File…. This will open the Certificate Export Wizard, click Next to get to the Export File Format screen.
  4. Choose DER encoded binary X.509 (.CER), click Next
  5. Click Browse… and save the file to your computer. Name it something descriptive. Click Next, then click Finish.
  6. Open Chrome settings, scroll to the bottom, and click Show advanced settings…
  7. Under HTTPS/SSL, click Manage certificates…
  8. Click the Trusted Root Certification Authorities tab, then click the Import… button. This opens the Certificate Import Wizard. Click Next to get to the File to Import screen.
  9. Click Browse… and select the certificate file you saved earlier, then click Next.
  10. Select Place all certificates in the following store. The selected store should be Trusted Root Certification Authorities. If it isn’t, click Browse… and select it. Click Next and Finish
  11. Compare fingerprints. Click Yes on the security warning.

When you restart Chrome, no more security warning and a green icon. If you don’t restart Chrome, no security warning but the red icon remains.

If you missed the question about the fingerprints, you can compare them later. In the list of certificates, click on the right one and click View, then visit Details and scroll down to Thumbprint.

Tags:

Comments on 2016-11-17 Monit

Oh, and I also noticed that my /etc/crontab only executed the hourly jobs because all other jobs checked for the existence of anacron which I don’t use. Whaaat.

– Alex Schroeder 2016-11-17 17:01 UTC

Add Comment

More...

Comments


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 the comment page if you need to fix typos. You can subscribe to new comments by email without leaving a comment.

To save this page you must answer this question:

Please say HELLO.

Referrers: $ cat /dev/brain > /dev/blog Gothridge Manor Planet Emacsen frothsof D&D