Changeset 329
- Timestamp:
- 09/03/07 03:54:09 (15 months ago)
- Location:
- veekun/trunk
- Files:
-
- 1 added
- 5 modified
-
lib/Vee/Controller/Dex/Utils.pm (modified) (3 diffs)
-
lib/Vee/Schema/PokeMoves.pm (modified) (1 diff)
-
lib/Vee/Schema/Pokemon.pm (modified) (2 diffs)
-
root/dex-images/tree.png (added)
-
templates/dex/page/pokemon.tt (modified) (7 diffs)
-
templates/dex/utils/backtrace.tt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
veekun/trunk/lib/Vee/Controller/Dex/Utils.pm
r303 r329 143 143 144 144 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; 148 162 149 163 $s->{page_title} = $pokemon->name . ' - ' . $move->name . ' parents'; … … 152 166 '<a href="' . $c->uri('Dex', 'pokemon_list') . '">Pokémon</a>', 153 167 '<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>', 155 170 ]; 156 171 … … 163 178 -nest => \ "FIND_IN_SET('$gen', version)", 164 179 }); 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; 166 181 167 182 my $gender_restriction; 168 183 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; 170 186 } 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({ 175 194 moveid => $move->id, 195 method => [qw[ level egg machine ]], 176 196 -nest => \ "FIND_IN_SET('$gen', version)", 177 197 'pokemon.gender' => $gender_restriction, 178 198 }, { 179 prefetch => { pokemon => 'breeds' }, 180 order_by => 'me.pokeid ASC', 181 group_by => [ 'pokemon.evid', 'breeds.breed' ], 199 join => 'pokemon', 182 200 }); 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); 185 225 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; 187 228 } 188 229 189 230 my %breeding_tree = ( $pokemon->id => {} ); 190 231 191 my %seen = ( $pokemon->id => 0 ); # hash of ( poke_id => tree_level, ... )232 my %seen = ( $pokemon->id => 0 ); # hash of ( poke_id => 1, ... ) 192 233 my $cur_level = 0; # current tree level 193 234 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! 195 257 196 258 do { 197 259 $cur_level++; 260 my %branches; 261 198 262 while (my $pokeid = shift @this_level) { 199 my $this_poke = $ learners{$pokeid}->pokemon;263 my $this_poke = $pokemon{$pokeid}; 200 264 my $hashref = shift @this_level; 201 265 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 221 326 $s->{breeding_tree} = \%breeding_tree; 327 $s->{pokemon} = \%pokemon; 328 $s->{learn_methods} = \%learn_methods; 329 $s->{optimal} = \@optimal_path; 222 330 223 331 $s->{template} = 'dex/utils/backtrace.tt'; -
veekun/trunk/lib/Vee/Schema/PokeMoves.pm
r31 r329 12 12 __PACKAGE__->add_columns("pokeid", "moveid", "level", "version", "method"); 13 13 14 __PACKAGE__->set_primary_key(qw/ pokeid moveid level version method /); 15 __PACKAGE__->add_unique_constraint([qw[ pokeid moveid level version method ]], undef); 16 14 17 __PACKAGE__->belongs_to(pokemon => 'Vee::Schema::Pokemon', 'pokeid'); 15 18 __PACKAGE__->belongs_to(move => 'Vee::Schema::Moves', 'moveid'); -
veekun/trunk/lib/Vee/Schema/Pokemon.pm
r307 r329 5 5 use strict; 6 6 use warnings; 7 8 7 use base 'DBIx::Class'; 9 8 … … 99 98 100 99 # 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; 102 101 delete $breeds{15}; 103 102 -
veekun/trunk/templates/dex/page/pokemon.tt
r324 r329 388 388 [% END %] 389 389 [% END %] 390 390 391 <!-- ####################################################################### --> 391 392 … … 404 405 [% END %] 405 406 406 [% version_headers = FOR ver IN header_icons %] 407 [% version_headers = BLOCK %] 408 <th class="level"></th> 409 [% FOR ver IN header_icons %] 407 410 <th class="level">[% Icons.$ver %]</th> 411 [% END %] 408 412 [% END %] 409 413 … … 442 446 [% FOREACH move IN moves.level %] 443 447 <tr class="color[% color %]"> 448 <td></td> 444 449 [% FOREACH ver IN move_columns %] 445 450 <td class="level"> [% move.versions.$ver ? ( move.versions.$ver == 1 ? '--' : move.versions.$ver ) : '' %] </td> … … 456 461 [% moveid = move.moveid %] 457 462 <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> 458 464 [% IF generation == 0 %] <td class="level"> </td>[% IF move_columns_inv.y %] <td class="level"> </td>[% END %][% END %] 459 465 [% IF generation <= 1 %] … … 479 485 [% moveid = move.moveid %] 480 486 <tr class="color[% color %]"> 487 <td></td> 481 488 [% IF generation == 0 %] <td> </td>[% IF move_columns_inv.y %] <td> </td>[% END %][% END %] 482 489 [% IF generation <= 1 %] … … 506 513 [% moveid = move.moveid %] 507 514 <tr class="color[% color %]"> 515 <td></td> 508 516 <td colspan="[% move_columns.size %]" class="level"> [% move.method %] </td> 509 517 [% move_cells(moveid) %] … … 519 527 [% moveid = move.moveid %] 520 528 <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> 521 530 [% IF generation == 0 %] 522 531 <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 1 1 [% PROCESS 'dex/common.tt' %] 2 2 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 %] 10 11 [% END %] 11 </li> 12 [% END %] 13 </ul> 12 </p> 14 13 [% END %] 15 14 16 [% INCLUDE breeding_branch branch=breeding_tree %] 15 <h1>Full Breeding Tree</h1> 17 16 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émon</th> 39 <th>Learns by</th> 40 [% pokemon_header %] 41 </tr> 42 [% breeding_branch(breeding_tree, 0, 1) %] 43 </table> 44
