From 59d18127c08f550775f7b608fbf759ad447d6a58 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 27 May 2014 01:26:03 -0400 Subject: rename the script i do want to expand this to not be entirely about fusions, so let's leave that open --- bin/smt | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bin/smt_fusion | 243 --------------------------------------------------------- 2 files changed, 243 insertions(+), 243 deletions(-) create mode 100644 bin/smt delete mode 100644 bin/smt_fusion diff --git a/bin/smt b/bin/smt new file mode 100644 index 0000000..292c807 --- /dev/null +++ b/bin/smt @@ -0,0 +1,243 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use Getopt::Long; +use List::Util 'max', 'sum0'; + +use Games::SMTNocturne::Demons; + +sub _demon { Games::SMTNocturne::Demons::demon(@_) } +sub _demons_of_type { Games::SMTNocturne::Demons::demons_of_type(@_) } +sub _fuse { Games::SMTNocturne::Demons::fuse(@_) } +sub _fusions_for { Games::SMTNocturne::Demons::fusions_for(@_) } + +my $command = shift @ARGV; + +my ($max_level, $deathstones, $kagatsuchi, $bosses); +GetOptions( + "max_level=i" => \$max_level, + "deathstones=i" => \$deathstones, + "kagatsuchi=i" => \$kagatsuchi, + "boss=s@" => \$bosses, +) or die "couldn't parse options"; +$bosses = [ split(/,/, join(',', @$bosses)) ] + if $bosses; + +my $options = { + max_level => $max_level, + deathstone => $deathstones, + kagatsuchi => $kagatsuchi, + bosses => $bosses, +}; + +if ($command !~ /^_/ && defined &$command) { + { no strict 'refs'; &{ $command }(@ARGV) } +} +else { + die "unknown command $command"; +} + +sub demon { + my ($demon) = @_; + print _demon($demon), "\n"; +} + +sub demons_of_type { + my ($type) = @_; + print join("\n", _demons_of_type($type)), "\n"; +} + +sub fuse { + my ($demon1, $demon2, $sacrifice) = @_; + print _fuse($demon1, $demon2, { sacrifice => $sacrifice, %$options }), "\n"; +} + +sub fusions_for { + my ($demon) = @_; + print join("\n", _fusions_for($demon, $options)), "\n"; +} + +sub min_level_for { + my ($demon) = @_; + + my @fusions = _fusions_for($demon, $options); + my $min_level = 99; + my @min_level_fusions; + for my $fusion (@fusions) { + my $max_level = max(map { $_->level } $fusion->all_demons); + if ($max_level < $min_level) { + @min_level_fusions = ($fusion); + $min_level = $max_level; + } + elsif ($max_level == $min_level) { + push @min_level_fusions, $fusion; + } + } + print "Level $min_level:\n"; + print join("\n", @min_level_fusions), "\n"; +} + +sub party_fusion { + my @demons = @_; + + my $seen = _party_fusion_recursive_fuse( + { + map { $_ => _demon($_) } @demons + }, + { + map { $_ => _demon($_) } @demons + }, + $deathstones, + map { _demon($_) } @demons + ); + my @found = + sort { $a->level <=> $b->level } + map { _demon($_) } + keys %$seen; + for my $demon (@found) { + my $path = $seen->{$demon->name}; + if (ref($path) eq 'ARRAY') { + print "$demon:\n"; + my ($fusion, $current) = @$path; + print " $_\n" for _linearize_tree($current, $fusion); + } + else { + print "$demon\n"; + } + } +} +my $SEEN = {}; +sub _party_fusion_recursive_fuse { + my ($seen, $current, $deathstones, @demons) = @_; + + return $seen if $SEEN->{join(';', @demons, ($deathstones || ''))}++; + + if (@demons > 1) { + for my $demon1 (@demons) { + for my $demon2 (grep { $_ ne $demon1 } @demons) { + if ($deathstones) { + for my $phase (0..8) { + _check_fusion( + $seen, $current, \@demons, + $demon1, $demon2, undef, + $deathstones, $phase + ); + } + } + else { + _check_fusion($seen, $current, \@demons, $demon1, $demon2); + } + for my $demon3 (grep { $_ ne $demon1 && $_ ne $demon2 } @demons) { + _check_fusion( + $seen, $current, \@demons, $demon1, $demon2, $demon3 + ); + } + } + } + } + + return $seen; +} +sub _check_fusion { + my ($seen, $current, $party, $demon1, $demon2, $sacrifice, $deathstones, $phase) = @_; + + my $fused = _fuse( + $demon1, $demon2, + { + %$options, + sacrifice => $sacrifice, + deathstone => $deathstones, + kagatsuchi => $phase, + }, + ); + return unless $fused; + return if defined $max_level && $fused->level > $max_level; + + my $new_current = { %$current }; + + my $new_fusion = Games::SMTNocturne::Demons::Fusion->new( + $options, + $demon1, $demon2, + ($sacrifice + ? ($sacrifice) + : ()), + ($fused->type eq 'Fiend' + ? ('', $phase) + : ()), + ); + if (my $old_fusion = $new_current->{$fused->name}) { + my $old_max_level = _max_level($new_current, $old_fusion); + my $new_max_level = _max_level($new_current, $new_fusion); + if ($new_max_level < $old_max_level) { + $new_current->{$fused->name} = $new_fusion; + } + else { + my $old_demon_count = _demon_count($new_current, $old_fusion); + my $new_demon_count = _demon_count($new_current, $new_fusion); + if ($new_demon_count < $old_demon_count) { + $new_current->{$fused->name} = $new_fusion; + } + } + } + else { + $new_current->{$fused->name} = $new_fusion; + } + if (my $old = $seen->{$fused->name}) { + my ($old_fusion, $old_current) = ref($old) eq 'ARRAY' + ? @$old : ($old, undef); + my $old_max_level = _max_level($old_current, $old_fusion); + my $new_max_level = _max_level($new_current, $new_fusion); + if ($new_max_level < $old_max_level) { + $seen->{$fused->name} = [ $new_fusion, $new_current ]; + } + else { + my $old_demon_count = _demon_count($old_current, $old_fusion); + my $new_demon_count = _demon_count($new_current, $new_fusion); + if ($new_demon_count < $old_demon_count) { + $seen->{$fused->name} = [ $new_fusion, $new_current ]; + } + } + } + else { + $seen->{$fused->name} = [ $new_fusion, $new_current ]; + } + + my @new_party = ( + $fused, + grep { + $_ ne $demon1 && $_ ne $demon2 && (!$sacrifice || $_ ne $sacrifice) + } @$party + ); + _party_fusion_recursive_fuse( + $seen, $new_current, + ($fused->type eq 'Fiend' ? $deathstones - 1 : $deathstones), + @new_party + ); +} +sub _max_level { + my ($seen, $fusion) = @_; + + return 0 if $fusion->isa('Games::SMTNocturne::Demons::Demon'); + return 1 + max( + map { _max_level($seen, $seen->{$_->name}) } $fusion->all_demons + ); +} +sub _demon_count { + my ($seen, $fusion) = @_; + + return 1 if $fusion->isa('Games::SMTNocturne::Demons::Demon'); + my @demons = $fusion->all_demons; + return @demons + sum0( + map { _demon_count($seen, $seen->{$_->name}) } @demons + ); +} +sub _linearize_tree { + my ($seen, $fusion) = @_; + + return if $fusion->isa('Games::SMTNocturne::Demons::Demon'); + return ( + (map { _linearize_tree($seen, $seen->{$_->name}) } $fusion->all_demons), + $fusion, + ); +} diff --git a/bin/smt_fusion b/bin/smt_fusion deleted file mode 100644 index 292c807..0000000 --- a/bin/smt_fusion +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use Getopt::Long; -use List::Util 'max', 'sum0'; - -use Games::SMTNocturne::Demons; - -sub _demon { Games::SMTNocturne::Demons::demon(@_) } -sub _demons_of_type { Games::SMTNocturne::Demons::demons_of_type(@_) } -sub _fuse { Games::SMTNocturne::Demons::fuse(@_) } -sub _fusions_for { Games::SMTNocturne::Demons::fusions_for(@_) } - -my $command = shift @ARGV; - -my ($max_level, $deathstones, $kagatsuchi, $bosses); -GetOptions( - "max_level=i" => \$max_level, - "deathstones=i" => \$deathstones, - "kagatsuchi=i" => \$kagatsuchi, - "boss=s@" => \$bosses, -) or die "couldn't parse options"; -$bosses = [ split(/,/, join(',', @$bosses)) ] - if $bosses; - -my $options = { - max_level => $max_level, - deathstone => $deathstones, - kagatsuchi => $kagatsuchi, - bosses => $bosses, -}; - -if ($command !~ /^_/ && defined &$command) { - { no strict 'refs'; &{ $command }(@ARGV) } -} -else { - die "unknown command $command"; -} - -sub demon { - my ($demon) = @_; - print _demon($demon), "\n"; -} - -sub demons_of_type { - my ($type) = @_; - print join("\n", _demons_of_type($type)), "\n"; -} - -sub fuse { - my ($demon1, $demon2, $sacrifice) = @_; - print _fuse($demon1, $demon2, { sacrifice => $sacrifice, %$options }), "\n"; -} - -sub fusions_for { - my ($demon) = @_; - print join("\n", _fusions_for($demon, $options)), "\n"; -} - -sub min_level_for { - my ($demon) = @_; - - my @fusions = _fusions_for($demon, $options); - my $min_level = 99; - my @min_level_fusions; - for my $fusion (@fusions) { - my $max_level = max(map { $_->level } $fusion->all_demons); - if ($max_level < $min_level) { - @min_level_fusions = ($fusion); - $min_level = $max_level; - } - elsif ($max_level == $min_level) { - push @min_level_fusions, $fusion; - } - } - print "Level $min_level:\n"; - print join("\n", @min_level_fusions), "\n"; -} - -sub party_fusion { - my @demons = @_; - - my $seen = _party_fusion_recursive_fuse( - { - map { $_ => _demon($_) } @demons - }, - { - map { $_ => _demon($_) } @demons - }, - $deathstones, - map { _demon($_) } @demons - ); - my @found = - sort { $a->level <=> $b->level } - map { _demon($_) } - keys %$seen; - for my $demon (@found) { - my $path = $seen->{$demon->name}; - if (ref($path) eq 'ARRAY') { - print "$demon:\n"; - my ($fusion, $current) = @$path; - print " $_\n" for _linearize_tree($current, $fusion); - } - else { - print "$demon\n"; - } - } -} -my $SEEN = {}; -sub _party_fusion_recursive_fuse { - my ($seen, $current, $deathstones, @demons) = @_; - - return $seen if $SEEN->{join(';', @demons, ($deathstones || ''))}++; - - if (@demons > 1) { - for my $demon1 (@demons) { - for my $demon2 (grep { $_ ne $demon1 } @demons) { - if ($deathstones) { - for my $phase (0..8) { - _check_fusion( - $seen, $current, \@demons, - $demon1, $demon2, undef, - $deathstones, $phase - ); - } - } - else { - _check_fusion($seen, $current, \@demons, $demon1, $demon2); - } - for my $demon3 (grep { $_ ne $demon1 && $_ ne $demon2 } @demons) { - _check_fusion( - $seen, $current, \@demons, $demon1, $demon2, $demon3 - ); - } - } - } - } - - return $seen; -} -sub _check_fusion { - my ($seen, $current, $party, $demon1, $demon2, $sacrifice, $deathstones, $phase) = @_; - - my $fused = _fuse( - $demon1, $demon2, - { - %$options, - sacrifice => $sacrifice, - deathstone => $deathstones, - kagatsuchi => $phase, - }, - ); - return unless $fused; - return if defined $max_level && $fused->level > $max_level; - - my $new_current = { %$current }; - - my $new_fusion = Games::SMTNocturne::Demons::Fusion->new( - $options, - $demon1, $demon2, - ($sacrifice - ? ($sacrifice) - : ()), - ($fused->type eq 'Fiend' - ? ('', $phase) - : ()), - ); - if (my $old_fusion = $new_current->{$fused->name}) { - my $old_max_level = _max_level($new_current, $old_fusion); - my $new_max_level = _max_level($new_current, $new_fusion); - if ($new_max_level < $old_max_level) { - $new_current->{$fused->name} = $new_fusion; - } - else { - my $old_demon_count = _demon_count($new_current, $old_fusion); - my $new_demon_count = _demon_count($new_current, $new_fusion); - if ($new_demon_count < $old_demon_count) { - $new_current->{$fused->name} = $new_fusion; - } - } - } - else { - $new_current->{$fused->name} = $new_fusion; - } - if (my $old = $seen->{$fused->name}) { - my ($old_fusion, $old_current) = ref($old) eq 'ARRAY' - ? @$old : ($old, undef); - my $old_max_level = _max_level($old_current, $old_fusion); - my $new_max_level = _max_level($new_current, $new_fusion); - if ($new_max_level < $old_max_level) { - $seen->{$fused->name} = [ $new_fusion, $new_current ]; - } - else { - my $old_demon_count = _demon_count($old_current, $old_fusion); - my $new_demon_count = _demon_count($new_current, $new_fusion); - if ($new_demon_count < $old_demon_count) { - $seen->{$fused->name} = [ $new_fusion, $new_current ]; - } - } - } - else { - $seen->{$fused->name} = [ $new_fusion, $new_current ]; - } - - my @new_party = ( - $fused, - grep { - $_ ne $demon1 && $_ ne $demon2 && (!$sacrifice || $_ ne $sacrifice) - } @$party - ); - _party_fusion_recursive_fuse( - $seen, $new_current, - ($fused->type eq 'Fiend' ? $deathstones - 1 : $deathstones), - @new_party - ); -} -sub _max_level { - my ($seen, $fusion) = @_; - - return 0 if $fusion->isa('Games::SMTNocturne::Demons::Demon'); - return 1 + max( - map { _max_level($seen, $seen->{$_->name}) } $fusion->all_demons - ); -} -sub _demon_count { - my ($seen, $fusion) = @_; - - return 1 if $fusion->isa('Games::SMTNocturne::Demons::Demon'); - my @demons = $fusion->all_demons; - return @demons + sum0( - map { _demon_count($seen, $seen->{$_->name}) } @demons - ); -} -sub _linearize_tree { - my ($seen, $fusion) = @_; - - return if $fusion->isa('Games::SMTNocturne::Demons::Demon'); - return ( - (map { _linearize_tree($seen, $seen->{$_->name}) } $fusion->all_demons), - $fusion, - ); -} -- cgit v1.2.3