package Enigmatic::CryptTrain;
use DAKKAR::p 'class';
use Enigmatic::Types qw(Letter RotorPos);
use MooseX::Types::Moose qw(ArrayRef);
use Moose::Util::TypeConstraints;
has rotors => (
isa => ArrayRef[class_type('Enigmatic::Rotor')],
traits => ['Array'],
writer => 'set_rotors',
handles => {
set_rotor => 'set',
rotor_at => 'get',
rotors => 'elements',
},
);
sub rotor_count {
my ($self) = @_;
return scalar $self->rotors;
}
has reflector => (
isa => class_type('Enigmatic::Reflector'),
writer => 'set_reflector',
reader => 'reflector',
);
has positions => (
isa => ArrayRef[RotorPos],
traits => ['Array'],
writer => 'set_positions',
handles => {
set_position => 'set',
position_at => 'get',
positions => 'elements',
},
);
sub map {
my $self = shift;
my ($letter) = pos_validated_list(
\@_,
{ isa => Letter },
);
for my $element (
$self->rotors,
$self->reflector,
(reverse $self->rotors),
) {
$letter = $element->map($letter);
}
$self->step_positions();
return $letter;
}
sub _inc_position {
my ($self,$idx) = @_;
my $cur = $self->position_at($idx);
$cur = ($cur+1)%26;
$self->set_position($idx,$cur);
return $cur;
}
sub rotor_window_at {
my ($self,$idx) = @_;
return ['A'..'Z']->[$self->position_at($idx)]
}
sub step_positions {
my ($self) = @_;
for my $pos (0 .. $self->rotor_count -1) {
$self->_inc_position($pos);
my $rot = $self->rotor_at($pos);
last
unless $rot->has_notch_at($self->rotor_window_at($pos));
}
return;
}