Number Plate Golf: Parsing

Ruby, 629 bytes

s=gets
p=c=0
'PETeRborOuGh
NORWich
IPSwicH
BIRMiNGHam
CARdiff
SWANsea
BAngOr
CHEsTer
SHREWsbURY
CHELmSFOrd
NOTTInGHAm
LInCOlN
MAIDStone
BRIgHtON
BOURNEMoUth
PORTSMoUth
ISLE OF WIGHt
PORtSmOUtH
BOREHAMwoOD
NORTHAmPton
WIMBLeDoN
BOREHAMwOoD
SIdcup
MANChESTer
NEWCAstle
STOcKtON
oXFOrd
PReStON
CArlISlE
ReADIng
GLAsGoW
EDINBUrGh
DUNdEe
ABERDEen
INvErNEsS
WORcESTer
EXeTeR
TRUro
BRiStoL
PERSONAL eXPOrt
LeEds
SHEFFiElD
BEVERLEY'.split($/).map{|w|c<s[0,2].to_i(35)&&p=w;d=0;w.bytes{|b|d+=d+b/95};c+=d}
y=(?1+s[2,9]).to_i-1
puts p.capitalize+", 1/%d/20%02d to %d/%d/20%02d"%[z=3+y%100/50*6,1+y%=50,z<4?31:y%4==2?29:28,(z+5)%12,1+y+z/9]

The first two letters are interpreted as a base 35 number and then looked up to find the correct town. The quantity of addresses assigned to each town is encoded (run-length encoding) in the captialization of each town in binary, where uppercase=0 and lowercase=1.

In order to handle the Y age indicator for the first half of 2001, a 1 is prepended to the age. Thus 1 Y is interpreted by .to_i as 1, and 1 02 is interpreted as 102. 1 is subtracted (to handle the wraparound for 2050) and taken modulo 100 to decide which half of the year we are in. It is then taken modulo 50 (and the 1 added on again) to find the year.


PHP, 1044 800 757 bytes

The script is added to a PHAR (PHP archive) and compressed with gzip to shrink it from 1044 to 800 bytes. Further optimization took the original code down to 994 bytes, which then as a Phar is 757 bytes. Takes the license plate string as the first argument like php u.phar BD51 SMR (actually the latter part is read as "second argument", but useless garbage anyway for this challenge).

Download the uklic.phar file for this answer

This is the script before compression:

<?$o=[X=>'Personal export',B=>Birmingham,E=>Chelmsford,M=>Manchester,O=>Oxford,R=>Reading,V=>Worcester,A=>['A-N'=>Peterborough,'O-U'=>Norwich,'V-Y'=>Ipswich],C=>['A-O'=>Cardiff,'P-V'=>Swansea,WXY=>Bangor],D=>['A-K'=>Chester,'L-Y'=>Shrewsbury],F=>['A-P'=>Nottingham,'R-Y'=>Lincoln],G=>['A-O'=>Maidstone,'P-Y'=>Brighton],H=>['A-J'=>Bournemouth,'K-Y'=>Portsmouth,W=>'Isle of Wight'],K=>['A-L'=>Borehamwood,'M-Y'=>Northampton],L=>['A-J'=>Wimbledon,'K-T'=>Borehamwood,'U-Y'=>Sidcup],N=>['A-O'=>Newcastle,'P-Y'=>Stockton],P=>['A-T'=>Preston,'U-Y'=>Carlisle],S=>['A-J'=>Glasgow,'K-O'=>Edinburgh,'P-T'=>Dundee,UVW=>Aberdeen,XY=>Inverness],W=>['A-J'=>Exeter,KL=>Truro,'M-Y'=>Bristol],Y=>['A-K'=>Leeds,'L-U'=>Sheffield,'V-Y'=>Beverley]];$p=$argv[1];$a=$o[$p{0}];$z=$a;if(count($a)>1){foreach($a as$r=>$d){if(preg_match("/[$r]/",$p{1})){$z=$d;}}}$n=$p{2}.$p{3};$y=$n+($n==Y);if($y==0)$y=50;$h=$y&&$n<51;$y%=50;$x=$y+1;printf('%s, 1/%s/20%02d to %s/20%02d',$z,$h?3:9,$y,$h?'31/8':28+!($x%4).'/2',$h?$y:$x);

The DVLA Office lookup is a simple map with a string if the first character of the license plate is enough to identify the office and an array if we need the second character. The character range is then matched with a simple regexp.

The code for the date range is quite easy except for the two edge cases "Y" and "00", which can't be calculated. Y is cast to integer and added a 1, in case the input is an integer. That's how we get 2001 for Y. 00 is accounted for by modulo arithmetic.

This is the slightly better readable version:

<?
$o = [
  X => 'Personal export',
  B => Birmingham,
  E => Chelmsford,
  M => Manchester,
  O => Oxford,
  R => Reading,
  V => Worcester,
  A => ['A-N' => Peterborough, 'O-U' => Norwich, 'V-Y' => Ipswich],
  C => ['A-O' => Cardiff, 'P-V' => Swansea, WXY => Bangor],
  D => ['A-K' => Chester, 'L-Y' => Shrewsbury],
  F => ['A-P' => Nottingham, 'R-Y' => Lincoln],
  G => ['A-O' => Maidstone, 'P-Y' => Brighton],
  H => ['A-J' => Bournemouth, 'K-Y' => Portsmouth, W => 'Isle of Wight'],
  K => ['A-L' => Borehamwood, 'M-Y' => Northampton],
  L => ['A-J' => Wimbledon, 'K-T' => Borehamwood, 'U-Y' => Sidcup],
  N => ['A-O' => Newcastle, 'P-Y' => Stockton],
  P => ['A-T' => Preston, 'U-Y' => Carlisle],
  S => ['A-J' => Glasgow, 'K-O' => Edinburgh, 'P-T' => Dundee, UVW => Aberdeen, XY => Inverness],
  W => ['A-J' => Exeter, 'KL' => Truro, 'M-Y' => Bristol],
  Y => ['A-K' => Leeds, 'L-U' => Sheffield, 'V-Y' => Beverley]
];
$p=$argv[1];

$a=$o[$p{0}];
$z=$a;
if (count($a)-1) {
    foreach($a as $r => $d) {
        if (preg_match('/['.$r.']/', $p{1})) {$z=$d; }
    }
}
$n=$p{2}.$p{3};
$y=$n+($n==Y);
if ($y==0) {
    $y=50;
}
$h=$y&&$n<51;
$y%=50;
$x=$y+1;
printf('%s, 1/%s/20%02d to %s/20%02d',$z,$h?3:9,$y,$h?'31/8':28+!($x%4).'/2',$h?$y:$x);

Updates:

  1. Seriously trimmed down the date calculation section and made use of printf, replaced is_array($a) with `count($a)>1
  2. Optimized the Phar bootstrapping