Changeset 329

Show
Ignore:
Timestamp:
09/03/07 03:54:09 (15 months ago)
Author:
eevee
Message:
 
Location:
veekun/trunk
Files:
1 added
5 modified

Legend:

Unmodified
Added
Removed
  • veekun/trunk/lib/Vee/Controller/Dex/Utils.pm

    r303 r329  
    143143     
    144144    my $gen = 'dp'; # TODO: :( 
    145      
    146     my $pokemon = $c->model('DBIC::Pokemon')->single({ name => $c->req->captures->[0] }, { prefetch => 'breeds' }); 
    147     my $move    = $c->model('DBIC::Moves')  ->single({ name => $c->req->args    ->[0] });  # assumed to be an egg move 
     145 
     146    my $pokemon_name = $c->req->captures->[0]; 
     147    my $move_name    = $c->req->args    ->[0]; 
     148 
     149    # For some reason, Catalyst::DispatchType::Chained doesn't url-decode either 
     150    # args or captures, so do it here; this is safe to do since valid move and 
     151    # Pokemon names will never ever contain percent signs..  I hope. 
     152    $pokemon_name =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; 
     153    $move_name    =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; 
     154     
     155    my $pokemon = $c->model('DBIC::Pokemon')->single({ name => $pokemon_name }, { prefetch => 'breeds' }); 
     156    my $move    = $c->model('DBIC::Moves')  ->single({ name => $move_name    }); 
     157 
     158    $c->vee_stop('No such Pokémon ', $pokemon_name, '.') 
     159        if not $pokemon; 
     160    $c->vee_stop('No such move ', $move_name, '.') 
     161        if not $move; 
    148162 
    149163    $s->{page_title} = $pokemon->name . ' - ' . $move->name . ' parents'; 
     
    152166        '<a href="' . $c->uri('Dex', 'pokemon_list') . '">Pok&eacute;mon</a>', 
    153167        '<a href="' . $c->uri('Dex', 'pokemon', lc $pokemon->name) . '">' . $pokemon->name . '</a>', 
    154         'Breeding chains for <a href="' . $c->uri('Dex', 'moves', lc $move->name) . '">' . $move->name . '</a>', 
     168        'Breeding chains', 
     169        '<a href="' . $c->uri('Dex', 'moves', lc $move->name) . '">' . $move->name . '</a>', 
    155170    ]; 
    156171 
     
    163178        -nest  => \ "FIND_IN_SET('$gen', version)", 
    164179    }); 
    165     $c->vee_stop($pokemon->name . " can't inherit " . $move->name . ".") unless $inheritable_ct; 
     180    $c->vee_stop('', $pokemon->name, " can't inherit ", $move->name, '.') unless $inheritable_ct; 
    166181     
    167182    my $gender_restriction; 
    168183    if ($pokemon->gender == 255) { 
    169         $gender_restriction = 255;  # must also be genderless, i.e. bred with Ditto 
     184        # must also be genderless, i.e. bred with Ditto 
     185        $gender_restriction = 255; 
    170186    } else { 
    171         $gender_restriction = { '!=' => [ 0, 255 ] };  # cannot be female or genderless 
    172     } 
    173      
    174     my $learners_rs = $c->model('DBIC::PokeMoves')->search({ 
     187        # cannot be single-gender or genderless 
     188        $gender_restriction = { -not_in => [ 0, 254, 255 ] }; 
     189    } 
     190 
     191    ### grab the methods by which any Pokemon learn the move 
     192 
     193    my $methods_rs = $c->model('DBIC::PokeMoves')->search({ 
    175194        moveid => $move->id, 
     195        method => [qw[ level egg machine ]], 
    176196        -nest => \ "FIND_IN_SET('$gen', version)", 
    177197        'pokemon.gender' => $gender_restriction, 
    178198    }, { 
    179         prefetch => { pokemon => 'breeds' }, 
    180         order_by => 'me.pokeid ASC', 
    181         group_by => [ 'pokemon.evid', 'breeds.breed' ], 
     199        join => 'pokemon', 
    182200    }); 
    183  
    184     my %learners; 
     201    my %learn_methods; 
     202    while (my $row = $methods_rs->next) { 
     203        my $method = ucfirst $row->method; 
     204        if ($row->method eq 'level') { 
     205            $method .= ' ' . $row->level; 
     206        } elsif ($row->method eq 'machine') { 
     207            $method = 'TM/HM'; 
     208        } 
     209        warn $method; 
     210        push @{ $learn_methods{ $row->pokeid } }, $method; 
     211    } 
     212 
     213    ### do a SEPARATE query to grab all the possible Pokemon at once 
     214    # (DBIC seems to get confused if I prefetch { pokemon => breeds } above) 
     215    # XXX: is this my fault or theirs? 
     216 
     217    my $learners_rs = $c->model('DBIC::Pokemon')->search({ 
     218        id => [ keys %learn_methods ], 
     219    }, { 
     220        prefetch => 'breeds', 
     221        order_by => 'me.id ASC', 
     222    }); 
     223 
     224    my (%pokemon); 
    185225    while (my $row = $learners_rs->next) { 
    186         $learners{ $row->pokeid } = $row;  # TODO: arrange better? 
     226        warn $row->id, ': ', join ' ', $row->breeds; 
     227        $pokemon{ $row->id } = $row; 
    187228    } 
    188229 
    189230    my %breeding_tree = ( $pokemon->id => {} ); 
    190231 
    191     my %seen = ( $pokemon->id => 0 );   # hash of ( poke_id => tree_level, ... ) 
     232    my %seen = ( $pokemon->id => 0 );   # hash of ( poke_id => 1, ... ) 
    192233    my $cur_level = 0;                  # current tree level 
    193234    my @this_level = %breeding_tree;    # array of ( poke_id, hashref, ... ) on this level 
    194     my @next_level;                     # array of ( poke_id, hashref, ... ) created for next level 
     235 
     236    ### build the breeding-chain tree 
     237    # What we need is a tree of ancestors that can learn the move and pass it 
     238    # down; it should look something like this: 
     239    # pokemon => { 
     240    #     a => { 
     241    #         b => {}, 
     242    #         c => {}, 
     243    #         d => { 
     244    #             e => {}, 
     245    #         }, 
     246    #     }, 
     247    #     f => { 
     248    #         g => {}, 
     249    #         d => [ref to former d], 
     250    #     }, 
     251    # } 
     252    # The rules are basically that: 
     253    # 1. Every Pokemon is a key in a hash 
     254    # 2. Each corresponding value is a similar hash, containing a list of 
     255    #    Pokemon that are compatible 
     256    # 3. No infinite looping! 
    195257 
    196258    do { 
    197259        $cur_level++; 
     260        my %branches; 
     261 
    198262        while (my $pokeid = shift @this_level) { 
    199             my $this_poke = $learners{$pokeid}->pokemon; 
     263            my $this_poke = $pokemon{$pokeid}; 
    200264            my $hashref = shift @this_level; 
    201265 
    202             my @fathers = grep { 
    203                 # must be breeding-compatible 
    204                 $this_poke->can_breed_with($_->pokemon) and 
    205  
    206                 # must not have already been seen in a higher tree level 
    207                 # (both for optimization and to avoid inf. looping) 
    208                 (not exists $seen{ $_->pokeid } or 
    209                  $seen{ $_->pokeid } >= $cur_level) 
    210             } values %learners; 
    211  
    212             for my $father (@fathers) { 
    213                 $hashref->{ $father->pokeid } = {}; 
    214                 $seen{ $father->pokeid } = $cur_level unless exists $seen{ $father->pokeid }; 
    215                 push @next_level, $father->pokeid, $hashref->{ $father->pokeid }; 
    216             } 
    217         } 
    218         @this_level = @next_level; 
    219     } while ($cur_level < 10 and scalar keys %learners); 
    220  
     266            for my $pokeid (keys %pokemon) { 
     267                next if not $this_poke->can_breed_with( $pokemon{$pokeid} ); 
     268 
     269                if (exists $seen{ $pokeid }) { 
     270                    # don't put anything if this Pokemon has been seen higher 
     271                    # in the chain; needless descending is bad 
     272                    $hashref->{ $pokeid } = undef 
     273                        unless $seen{ $pokeid } < $cur_level; 
     274                } else { 
     275                    $hashref->{ $pokeid } = ( $branches{$pokeid} ||= {} ); 
     276                    $seen{ $pokeid } = $cur_level; 
     277                } 
     278            } 
     279        } 
     280 
     281        @this_level = %branches; 
     282    } while ($cur_level < 10 and @this_level); 
     283 
     284    ### find the optimal path 
     285     
     286    # since there should only be a small finite number of Pokemon, we can just 
     287    # iterate over every possibility; flatten into arrays, and take whichever 
     288    # is shortest 
     289 
     290    # TODO: merge this with the actual building of the tree, and toss useless 
     291    # branches as we go; right now this sub MODIFIES THE TREE, pruning any 
     292    # branches it finds that don't end with a level; this makes the dupe 
     293    # removal up top a little silly! 
     294    my $flattener; $flattener = sub { 
     295        my ($branch) = @_; 
     296        my @subchains; 
     297 
     298        for my $key (keys %$branch) { 
     299            if (grep /level/i, @{ $learn_methods{$key} }) { 
     300                # consider this Pokemon an endpoint if it learns the move 
     301                # naturally; TMs don't count since they are of limited quantity 
     302                push @subchains, [ $key ]; 
     303                $branch->{$key} &&= {}; 
     304                next; 
     305            } 
     306 
     307            # ...otherwise, only add more chains if this branch has any valid 
     308            # subchains of its own 
     309            my @child_chains = $flattener->( $branch->{$key} ); 
     310            if (@child_chains) { 
     311                push @subchains, map [ $key, @$_ ], @child_chains; 
     312            } else { 
     313                delete $branch->{$key}; 
     314            } 
     315        } 
     316 
     317        return @subchains; 
     318    }; 
     319 
     320    # only take the shortest chains; it's possible there could be several! 
     321    my @flat_chains  = sort { $#$a <=> $#$b } $flattener->( \%breeding_tree ); 
     322    my @optimal_path = grep { $#$_ == $#{ $flat_chains[0] } } @flat_chains; 
     323    @$_ = reverse @$_ for @optimal_path; 
     324     
     325    ### cram everything important into the stash 
    221326    $s->{breeding_tree} = \%breeding_tree; 
     327    $s->{pokemon}       = \%pokemon; 
     328    $s->{learn_methods} = \%learn_methods; 
     329    $s->{optimal}       = \@optimal_path; 
    222330 
    223331    $s->{template} = 'dex/utils/backtrace.tt'; 
  • veekun/trunk/lib/Vee/Schema/PokeMoves.pm

    r31 r329  
    1212__PACKAGE__->add_columns("pokeid", "moveid", "level", "version", "method"); 
    1313 
     14__PACKAGE__->set_primary_key(qw/ pokeid moveid level version method /); 
     15__PACKAGE__->add_unique_constraint([qw[ pokeid moveid level version method ]], undef); 
     16 
    1417__PACKAGE__->belongs_to(pokemon => 'Vee::Schema::Pokemon', 'pokeid'); 
    1518__PACKAGE__->belongs_to(move => 'Vee::Schema::Moves', 'moveid'); 
  • veekun/trunk/lib/Vee/Schema/Pokemon.pm

    r307 r329  
    55use strict; 
    66use warnings; 
    7  
    87use base 'DBIx::Class'; 
    98 
     
    9998 
    10099    # breeds cannot appear twice due to keying, so this is guaranteed to work 
    101     $breeds{$_}++ for map $poke1->breeding_groups, $poke2->breeding_groups; 
     100    $breeds{$_}++ for $poke1->breeding_groups, $poke2->breeding_groups; 
    102101    delete $breeds{15}; 
    103102 
  • veekun/trunk/templates/dex/page/pokemon.tt

    r324 r329  
    388388[% END %] 
    389389[% END %] 
     390 
    390391<!-- ####################################################################### --> 
    391392 
     
    404405[% END %] 
    405406 
    406 [% version_headers = FOR ver IN header_icons %] 
     407[% version_headers = BLOCK %] 
     408    <th class="level"></th> 
     409[%     FOR ver IN header_icons %] 
    407410<th class="level">[% Icons.$ver %]</th> 
     411[%     END %] 
    408412[% END %] 
    409413 
     
    442446[% FOREACH move IN moves.level %] 
    443447<tr class="color[% color %]"> 
     448 <td></td> 
    444449[%     FOREACH ver IN move_columns %] 
    445450 <td class="level"> [% move.versions.$ver ? ( move.versions.$ver == 1 ? '--' : move.versions.$ver ) : '' %] </td> 
     
    456461[%         moveid = move.moveid %] 
    457462<tr class="color[% color %]"> 
     463 <td><a href="[% dex_uri('pokemon', this.name) %]/backtrace/[% MoveData.$moveid.name | lower %]"><img src="/dex-images/tree.png" alt="Chains" title="Breeding chains"/></a></td> 
    458464[%         IF generation == 0 %] <td class="level"> </td>[% IF move_columns_inv.y %] <td class="level"> </td>[% END %][% END %] 
    459465[%         IF generation <= 1 %] 
     
    479485[%         moveid = move.moveid %] 
    480486<tr class="color[% color %]"> 
     487 <td></td> 
    481488[%         IF generation == 0 %] <td> </td>[% IF move_columns_inv.y %] <td> </td>[% END %][% END %] 
    482489[%         IF generation <= 1 %] 
     
    506513[%         moveid = move.moveid %] 
    507514<tr class="color[% color %]"> 
     515 <td></td> 
    508516 <td colspan="[% move_columns.size %]" class="level"> [% move.method %] </td> 
    509517[%         move_cells(moveid) %] 
     
    519527[%         moveid = move.moveid %] 
    520528<tr class="color[% color %]"> 
     529 <td>[% IF move.versions.dp %]<a href="[% dex_uri('pokemon', this.name) %]/backtrace/[% MoveData.$moveid.name | lower %]"><img src="/dex-images/tree.png" alt="Chains" title="Breeding chains"/></a>[% END %]</td> 
    521530[%         IF generation == 0 %] 
    522531 <td colspan="[% move_columns_inv.y ? 2 : 1 %]">[% IF move.versions.rb; tm_name(MoveTMs.$moveid.0); END %]</td> 
  • veekun/trunk/templates/dex/utils/backtrace.tt

    r37 r329  
    11[% PROCESS 'dex/common.tt' %] 
    22 
    3 [% BLOCK breeding_branch %][%# branch %] 
    4 <ul> 
    5 [%     FOREACH pokemon IN branch.keys %] 
    6 <li> 
    7 [% pokemon %]: [%+ PokemonNames.$pokemon %] 
    8 [%     IF branch.$pokemon.keys.size %] 
    9 [%         INCLUDE breeding_branch branch=branch.$pokemon %] 
     3<h1>Optimal Chains</h1> 
     4[% FOREACH chain IN optimal %] 
     5<p> 
     6[%     FOREACH pokeid IN chain %] 
     7<a href="[% dex_uri('pokemon', pokemon.$pokeid.name) %]" class="dex-pokelist"><img src="/dex-images/icons/[% pokeid.pad(3) %].png" alt="[% pokemon.$pokeid.name %]"/></a> 
     8[%         UNLESS loop.last %] 
     9<img src="/images/see-also.png" alt="to"/> 
     10[%         END %] 
    1011[%     END %] 
    11 </li> 
    12 [%     END %] 
    13 </ul> 
     12</p> 
    1413[% END %] 
    1514 
    16 [% INCLUDE breeding_branch branch=breeding_tree %] 
     15<h1>Full Breeding Tree</h1> 
    1716 
     17[%# bypass MACRO's implicit my-ing of any assigned var %] 
     18[% color_hack = [1] %] 
     19 
     20[% MACRO breeding_branch(branch, depth) BLOCK %] 
     21[%     FOREACH pokeid IN branch.keys %] 
     22<tr class="color[% color_hack.0 %]"> 
     23[%         color_hack.0 = 3 - color_hack.0 %] 
     24    <td style="padding-left: [% depth * 32 %]px;"><a href="[% dex_uri('pokemon', pokemon.$pokeid.name) %]"><img src="/dex-images/icons/[% pokeid.pad(3) %].png" alt=""/> [% pokemon.$pokeid.name %]</a></td> 
     25    [%# TODO: lol I am being so lazy with these 8V %] 
     26    <td class="center">[% learn_methods.$pokeid.join('<br/>') %]</td> 
     27[%         pokemon_cells(pokemon.$pokeid) %] 
     28</tr> 
     29[%         IF branch.$pokeid.keys.size %] 
     30[%             next_depth = depth + 1 %] 
     31[%             breeding_branch(branch.$pokeid, next_depth) %] 
     32[%         END %] 
     33[%     END %] 
     34[% END %] 
     35 
     36<table class="dex-table dex-pokemon" cellspacing="0"> 
     37<tr class="heading"> 
     38    <th>Pok&eacute;mon</th> 
     39    <th>Learns by</th> 
     40[% pokemon_header %] 
     41</tr> 
     42[% breeding_branch(breeding_tree, 0, 1) %] 
     43</table> 
     44