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; }