Skip to content

Instantly share code, notes, and snippets.

@RogerDodger
Created March 26, 2019 08:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RogerDodger/0d0c4242a3d0362bcaf4ebd74299950e to your computer and use it in GitHub Desktop.
Save RogerDodger/0d0c4242a3d0362bcaf4ebd74299950e to your computer and use it in GitHub Desktop.
Calculate Lempel-Ziv complexity of beatmap note intervals
use 5.01;
use warnings;
use strict;
use Data::Dump;
for my $bm (glob "beatmaps/*") {
say "\n### " . substr($bm, 9) . " ###";
my $deltas = beatmap_deltas($bm);
say "Length: " . sum($deltas) / 1000 . "s";
say "Intervals: " . ($#$deltas + 1);
say "Lempel-Ziv: " . (my $lz = lempel_ziv($deltas));
say "LZ / Intervals: " . (sprintf "%.2f (%%)", my $ratio = 100 * $lz / ($#$deltas + 1));
}
sub beatmap_deltas {
open my $fh, "<", shift or warn $! and return;
my $prev;
my $label = '';
my @deltas;
while (my $line = <$fh>) {
if ($line =~ /^\[(.+)\]/) {
$label = $1;
}
elsif ($label eq 'HitObjects' and $line =~ /^[^,]+,[^,]+,(\d+)/) {
if ($prev) {
my $delta = $1 - $prev;
# We consider deltas longer than 4 seconds to be breaks
push @deltas, $delta if $delta < 4000;
}
$prev = $1;
}
}
\@deltas;
}
sub lempel_ziv {
my @S = @{ +shift };
my $i = 0;
my $C = 1;
my $u = 1;
my $v = 1;
my $vmax = $v;
while ($u + $v <= $#S) {
# We consider intervals within 2ms of each other equal
if (abs($S[$i + $v] - $S[$u + $v]) <= 2) {
$v += 1;
}
else {
$vmax = $v if $v > $vmax;
$i += 1;
if ($i == $u) {
$C += 1;
$u += $vmax;
$v = 1;
$i = 0;
$vmax = $v;
}
else {
$v = 1;
}
}
}
$C += 1 if $v != 1;
return $C;
}
sub sum {
my $n = 0;
$n += $_ for @{ +shift };
$n;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment