User:Ais523/jumble1.pl
Jump to navigation
Jump to search
use warnings; use strict; $|=1; our %assets; our $silent = 0; # Machine name format: Name* [Widget] (Storage) # Heats the given machine. Returns a reference to its name. sub heat_machine { my $looking_for = shift; $looking_for =~ /['*]/ and die "When heating a machine, you should specify the cold form"; for my $i (0..$assets{machine}->$#*) { my $machine = \$assets{machine}[$i]; my $cold_machine = $$machine; $cold_machine =~ s/[']//g; $cold_machine eq $looking_for or next; $$machine =~ s/'/*/ and return $machine; $$machine =~ s/ \[Cooling Widget\]/' [Cooling Widget]/ and return $machine; $$machine =~ s/ \(/* (/ and return $machine; $$machine =~ s/$/*/ and return $machine; } die "Did not have a Cold/Warm $looking_for to heat"; } # Removes all of the given Thing. Returns the number removed. sub consume_all_things { my $target = shift; my @new_thing_array; my $removed_count; for my $thing ($assets{thing}->@*) { if ($thing eq $target) { ++$removed_count; } else { push @new_thing_array, $thing; } } $assets{thing} = \@new_thing_array; $removed_count } # A pure function. Determines whether the given machine/widget is High Tech. sub is_high_tech { my $m_or_w = shift; $m_or_w =~ /^Chime\b/ and return 1; return; } # Determines whether the given machine/widget is unavailable due to being High Tech. sub is_unavailable_tech { my $m_or_w = shift; return unless is_high_tech $m_or_w; return if grep ($m_or_w =~ /^\Q$_\E\b/), $assets{tech}->@*; return 1; } # A pure function. Outputs the cost of the machine or widget. sub machine_or_widget_cost { my $m_or_w = shift; my $param = shift; $m_or_w =~ /^Axe\b/ and return [box => $param * 2]; $m_or_w =~ /^Baker\b/ and return [cog => 1]; if ($m_or_w =~ /^Chime\b/) { 10 == scalar @$param or die "A Chime costs 10 Things"; my %seen; $seen{$_}++ and die "A Chime costs 10 different Things (duplicated Thing: $_)" for @$param; return [map ((thing => $_), @$param)]; } $m_or_w =~ /^Clink\b/ and return [box => $param]; if ($m_or_w =~ /^Compactor\b/) { scalar @$param or die "You cannot compact 0 Things"; return [map ((thing => $_), @$param)]; } $m_or_w =~ /^Crusher\b/ and return [energy => 1, machine => $param]; $m_or_w =~ /^Dustsheet\b/ and die "Dustsheet unimplemented, because its buy cost and use cost are different"; $m_or_w =~ /^Glue Gun\b/ and return [box => $param->[0], box => $param->[1]]; $m_or_w =~ /^Greenhouse\b/ and return [energy => 1, thing => "$param Seed"]; $m_or_w =~ /^Harvester\b/ and return [cog => 2]; $m_or_w =~ /^Mill\b/ and return [cog => 1]; $m_or_w =~ /^Press\b/ and return [energy => 1]; $m_or_w =~ /^Printer\b/ and return [box => $param]; if ($m_or_w =~ /^Prototyper\b/) { $param > $assets{proto} or die "With $assets{proto} Prototypes, a $param box is too small to pay the Prototyper's cost"; return [cog => $assets{proto}, box => $param]; } $m_or_w =~ /^Seeder\b/ and return [cog => 1]; $m_or_w =~ /^Spin\b/ and return [energy => 1]; $m_or_w =~ /^Storage\b/ and return [box => $param]; $m_or_w =~ /^Sugarcuber\b/ and return [cog => 1]; $m_or_w =~ /^Wastebin\b/ and return [energy => 1]; $m_or_w eq 'Cooling Widget' and return [cog => 10]; $m_or_w eq 'Boxing Widget' and return [cog => 5]; die "Unrecognised machine/widget '$m_or_w'"; } # Outputs the items produced by the machine. May have side effects (e.g. consuming Sugar for a Sugarcuber). # The machine should be heated before calling this function – its name may be overwritten via $$_[0]. sub machine_effect { my $mref = shift; my $machine = $$mref; my $param = shift; $machine =~ /^Axe\b/ and return [box => $param, box => $param]; $machine =~ /^Baker\b/ and return [map ((thing => 'Bread'), 1..consume_all_things('Flour'))]; if ($machine =~ /^Chime\b/) { $assets{proto} < 11 and return [proto => 2]; return [victory => 1]; } $machine =~ /^Clink\b/ and return [cog => $param]; $machine =~ /^Compactor\b/ and return [box => scalar @$param]; $machine =~ /^Crusher\b/ and die "Crusher usage unimplemented, due to difficulty in calculating the minimum cost"; $machine =~ /^Glue Gun\b/ and return [box => $param->[0] + $param->[1]]; $machine =~ /^Greenhouse(['*])(?: \(.*\))?(.*)$/ and $$mref = "Greenhouse$1 ($param)$2" and return []; if ($machine =~ /^Harvester\b/) { my @output; for my $other_machine ($assets{machine}->@*) { $other_machine =~ /^Greenhouse\* \((.*?)\)/ and push @output, thing => $1, thing => "$1 Seed"; } return \@output; } $machine =~ /^Mill\b/ and return [map ((thing => 'Flour'), 1..consume_all_things('Wheat'))]; if ($machine =~ /^Press\b/) { $param >= 1 && $param <= 5 or die "You can only Press a box in the 1..5 range"; return [box => $param]; } $machine =~ /^Printer\b/ and return [map ((thing => 'Paper'), 1..((($param&1)+$param)/2))]; $machine =~ /^Prototyper\b/ and return [proto => 1]; $machine =~ /^Seeder\b/ and return [thing => "$param Seed"]; $machine =~ /^Spin\b/ and die "Spin usage unimplemented, because it has random effects"; $machine =~ /^Storage(['*]) \((\d+)\)(.*)$/ and $$mref = "Storage$1 ($param)$3" and return [box => $2]; $machine =~ /^Sugarcuber\b/ and return [map ((thing => 'Sugarcube'), 1..consume_all_things('Sugar'))]; $machine =~ /^Wastebin\b/ and die "Wastebin usage unimplemented, because it has random effects"; die "Unrecognised warm/hot machine '$machine'"; } # A pure function. Gives a text representation of an action parameter. sub param_as_text { my $machine = shift; my $param = shift; local $" = ", "; $machine =~ /^Axe\b/ and return "(using a ".($param*2)."-Box)"; $machine =~ /^Chime\b/ and return "(spending @$param)"; $machine =~ /^Clink\b/ and return "(using a $param-Box)"; $machine =~ /^Compactor\b/ and return "(spending @$param)"; $machine =~ /^Crusher\b/ and return "(spending a $param)"; $machine =~ /^Glue Gun\b/ and return "(using a ".$param->[0]."-Box and a ".$param->[1]."-Box)"; $machine =~ /^Greenhouse\b/ and return "(using a $param Seed)"; $machine =~ /^Press\b/ and return "(making a $param-Box)"; $machine =~ /^Printer\b/ and return "(using a $param-Box)"; $machine =~ /^Prototyper\b/ and return "(spending a $param-Box)"; $machine =~ /^Seeder\b/ and return "(producing a $param Seed)"; $machine =~ /^Storage\b/ and return "(using a $param-Box)"; die "Unexpected parameter for action involving machine '$machine'"; } sub spend_assets { my @to_spend = shift->@*; while (@to_spend) { my $asset_type = shift @to_spend; my $what = shift @to_spend; if (ref $assets{$asset_type}) { my $found = 0; my @new_list; for my $asset ($assets{$asset_type}->@*) { if (!$found && $asset eq $what) { $found = 1; } else { push @new_list, $asset; } } $found or die "Trying to spend nonexistent $asset_type $what"; $assets{$asset_type} = \@new_list; } else { $assets{$asset_type} >= $what or die "Trying to spend $asset_type x $what, but only $assets{$asset_type} are available"; $what > 0 or die "Trying to spend a negative amount $what of $asset_type"; $assets{$asset_type} -= $what; } } } sub gain_assets { my @to_gain = shift->@*; while (@to_gain) { my $asset_type = shift @to_gain; my $what = shift @to_gain; if (ref $assets{$asset_type}) { push $assets{$asset_type}->@*, $what; } else { $what > 0 or die "Trying to gain a negative amount $what of $asset_type"; $assets{$asset_type} += $what; } } scalar $assets{machine}->@* > 6 and die "There is a limit of 6 Machines on the Line"; scalar $assets{thing}->@* > 10 and die "There is a limit of 10 Things (explicitly specify which new Things to keep)"; } $\ = $/; print '{| class="wikitable"'; print "! Boxes || Energy || Cogs || Proto || Machines || Things || Tech || Action"; # XSS warning: the argument is wikitext, not plain text. sub print_comment_wikitext { my $comment_wikitext = shift; $silent or print "|-"; $silent or print "| colspan = 8 | $comment_wikitext"; } sub print_assets { my $action = shift; my $machine = shift; my $param = shift; my $keeping = shift; my @sorted_things = sort $assets{thing}->@*; my @grouped_things; for my $thing (@sorted_things) { if (@grouped_things && $grouped_things[-1] =~ /^\Q$thing\E(?: x (\d+))?$/) { my $quan = $1 || 1; $quan++; $grouped_things[-1] = "$thing x $quan"; } else { push @grouped_things, $thing; } } my @machines = $assets{machine}->@*; my @grouped_machines; for my $machine (@machines) { if (@grouped_machines && $grouped_machines[-1] =~ /^\Q$machine\E(?: x (\d+))?$/) { my $quan = $1 || 1; $quan++; $grouped_machines[-1] = "$machine x $quan"; } else { push @grouped_machines, $machine; } } local $" = ", "; my $boxes = $assets{box}->@* ? "@{$assets{box}}" : "none"; defined $param and $action .= " " . param_as_text($machine, $param); defined $keeping and $action .= " (Thing limit reached: keeping @$keeping)"; $silent or print "|-"; $silent or print "| $boxes || $assets{energy} || $assets{cog} || $assets{proto} || @grouped_machines || @grouped_things || @{$assets{tech}} || $action"; $assets{victory} and print_comment_wikitext "VICTORY!" and $assets{victory} = 0; } our $action_count = 0; sub cycle { $assets{box} = []; $assets{energy} = 2 if $assets{energy} < 2; s/['*]// for $assets{machine}->@*; $silent or print_comment_wikitext "End of cycle. Number of dynastic actions performed today: $action_count."; print_assets "Cycle Action"; $action_count = 0; } sub buy { my $machine = shift; my $param = shift; die "Cannot buy High Tech $machine that isn't on the tech list" if is_unavailable_tech $machine; spend_assets(machine_or_widget_cost($machine, $param)); gain_assets([machine => ($machine eq 'Storage' ? "$machine ($param)" : $machine)]); print_assets "Buy $machine", $machine, $param; ++$action_count; } sub demolish { my $machine = shift; $machine =~ /['*]/ and die "Only Cold Machines can be Demolished (trying to demolish $machine)"; spend_assets([machine => $machine]); print_assets "Demolish $machine"; ++$action_count; } sub activate { my $machine = shift; my $param = shift; my $keeping = shift; spend_assets(machine_or_widget_cost($machine, $param)); die "Cannot activate High Tech $machine that isn't on the tech list" if is_unavailable_tech $machine; my $mref = heat_machine $machine; my $outputs = machine_effect $mref, $param; if ($keeping) { # Check that the things we're keeping were actually generated. my @keeping = @$keeping; my @orig_outputs = @$outputs; my @kept_outputs; while (@orig_outputs) { my $asset_type = shift @orig_outputs; my $what = shift @orig_outputs; if ($asset_type ne 'thing') { push @kept_outputs, $asset_type, $what; next; } my @new_keeping; while (@keeping) { my $kept_thing = shift @keeping; if ($kept_thing eq $what) { push @kept_outputs, thing => $kept_thing; last; } push @new_keeping, $kept_thing; } push @keeping, @new_keeping; } local $" = ", "; @keeping and die "Trying to keep @keeping which is/are not in the outout"; $outputs = \@kept_outputs; } gain_assets $outputs; $keeping and 10 != scalar $assets{thing}->@* and die "When keeping only a subset, you must have exactly 10 Things"; print_assets "Activate $machine", $machine, $param, $keeping; ++$action_count; } sub spend { my $thing = shift; my $param = shift; spend_assets([thing => $thing]); if ($thing eq 'Sugarcube') { $param < 1 || $param > 7 and die "A Sugarcube can only generate a box with value 1..7"; gain_assets([box => $param]); print_assets "Spend $thing (making a $param-Box)"; return; } elsif ($thing eq 'Bread') { gain_assets([energy => 1]); } elsif ($thing eq 'Corn') { gain_assets([cog => 2]); } else { die "Unrecognised or unspendable thing $thing"; } print_assets "Spend $thing"; ++$action_count; } sub attach { my $machine = shift; my $widget = shift; die "Cannot buy High Tech $widget that isn't on the tech list" if is_unavailable_tech $widget; spend_assets(machine_or_widget_cost $widget); for my $i (0..$assets{machine}->$#*) { if ($assets{machine}[$i] eq $machine) { $assets{machine}[$i] =~ s/ \[.*?\]//; $assets{machine}[$i] =~ /^(.*?)((?: \(.*\))?)$/; $assets{machine}[$i] = "$1 [$widget]$2"; print_assets "Attach $widget to $machine"; ++$action_count; return; } } die "Could not find a $machine to attach a $widget to"; } sub buy_tech { my $m_or_w = shift; my $param = shift; die "Cannot buy Low Tech $m_or_w for the tech list, only High Tech machines/widgets can be there" unless is_high_tech $m_or_w; spend_assets(machine_or_widget_cost $m_or_w, $param); push $assets{tech}->@*, $m_or_w; die "You cannot have more Tech ([@{$assets{tech}}]) than Prototypes ($assets{proto})" unless $assets{proto} >= scalar $assets{tech}->@*; print_assets "Buy Tech $m_or_w"; ++$action_count; } # %assets = ( # box => [], # energy => 2, # cog => 16, # proto => 2, # machine => ['Clink', 'Harvester [Cooling Widget]', 'Seeder', 'Seeder', 'Greenhouse', 'Greenhouse'], # thing => ['Sugarcube', 'Sugar Seed', 'Sugar Seed', 'Wheat', 'Wheat', 'Wheat', 'Wheat', 'Wheat Seed', 'Corn Seed'], # tech => [], # victory => 0, # ); # print_assets "Initial state"; # demolish "Seeder"; # demolish "Seeder"; # activate "Greenhouse", "Wheat"; # activate "Greenhouse", "Corn"; # spend "Sugarcube", 7; # activate "Clink", 7; # activate "Harvester [Cooling Widget]"; # spend "Corn"; # buy "Compactor", ["Sugar Seed", "Corn Seed"]; # demolish "Compactor"; # activate "Harvester [Cooling Widget]", undef, # ["Wheat Seed", "Corn Seed", "Wheat"]; # buy "Mill"; # activate "Mill"; # buy "Baker"; # activate "Baker"; # cycle; %assets = ( box => [], energy => 2, cog => 17, proto => 2, machine => ['Clink', 'Harvester [Cooling Widget]', 'Greenhouse (Wheat)', 'Greenhouse (Corn)', 'Mill', 'Baker'], thing => ['Sugar Seed', 'Bread', 'Bread', 'Bread', 'Bread', 'Bread', 'Bread', 'Wheat Seed', 'Wheat Seed', 'Corn Seed'], tech => [], victory => 0, ); print_assets "Initial state"; demolish "Clink"; demolish "Mill"; demolish "Baker"; spend "Bread" for 1..6; buy "Greenhouse", "Sugar"; activate "Greenhouse (Wheat)", "Wheat"; activate "Greenhouse (Corn)", "Wheat"; activate "Greenhouse", "Corn"; activate "Harvester [Cooling Widget]"; buy "Greenhouse", "Wheat"; activate "Greenhouse", "Corn"; spend "Corn"; activate "Harvester [Cooling Widget]", undef, ["Wheat Seed", "Wheat", "Wheat", "Corn Seed", "Corn Seed", "Corn", "Corn"]; spend "Corn" for 1..2; buy "Compactor", ["Wheat Seed", "Wheat Seed", "Corn Seed", "Corn Seed"]; demolish "Compactor"; buy "Crusher", "Harvester* [Cooling Widget]"; demolish "Crusher"; buy "Harvester"; attach "Harvester", "Cooling Widget"; activate "Harvester [Cooling Widget]", undef, ["Wheat Seed", "Corn Seed", "Wheat", "Wheat", "Corn", "Corn"]; spend "Corn" for 1..2; buy "Compactor", ["Wheat Seed", "Corn Seed"]; demolish "Compactor"; activate "Harvester [Cooling Widget]", undef, ["Wheat", "Wheat", "Corn", "Corn"]; spend "Corn" for 1..2; buy "Crusher", "Harvester* [Cooling Widget]"; demolish "Crusher"; buy "Mill"; activate "Mill"; buy "Baker"; activate "Baker"; spend "Bread" for 1..8; buy "Crusher", "Mill*"; demolish "Crusher"; buy "Crusher", "Baker*"; demolish "Crusher"; buy "Harvester"; activate "Harvester"; spend "Corn" for 1..2; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Compactor", ["Wheat Seed", "Wheat Seed"]; demolish "Compactor"; buy "Greenhouse", "Corn"; activate "Greenhouse", "Corn"; sub cog_generation_loop { buy "Harvester"; activate "Harvester", undef, ["Wheat Seed", "Wheat Seed", "Wheat", "Wheat", "Corn Seed", "Corn", "Corn", "Corn"]; spend "Corn" for 1..3; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Compactor", ["Wheat Seed", "Wheat Seed", "Corn Seed"]; demolish "Compactor"; buy "Harvester"; activate "Harvester", undef, ["Wheat", "Wheat", "Corn Seed", "Corn", "Corn", "Corn"]; spend "Corn" for 1..3; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Compactor", ["Corn Seed"]; demolish "Compactor"; buy "Mill"; activate "Mill"; buy "Crusher", "Mill*"; demolish "Crusher"; buy "Baker"; activate "Baker"; spend "Bread" for 1..6; buy "Crusher", "Baker*"; demolish "Crusher"; buy "Harvester"; activate "Harvester"; spend "Corn" for 1..3; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Compactor", ["Wheat Seed", "Wheat Seed", "Corn Seed", "Corn Seed", "Corn Seed"]; demolish "Compactor"; } print_comment_wikitext "Main loop starts here."; cog_generation_loop; print_comment_wikitext "I'm now back where I was at the start of the main loop, but with 2 more Cogs and 1 more Energy. So I can do the loop again:"; cog_generation_loop; my $extra_main_loop_iterations = 150; print_comment_wikitext "This loop can be repeated arbitrarily many times to generate arbitrary amounts of Cogs and Energy. $extra_main_loop_iterations more iterations of the loop have been elided."; { local $silent = 1; cog_generation_loop for 1..$extra_main_loop_iterations; } print_assets "State after another $extra_main_loop_iterations main loop iterations"; buy "Seeder"; attach "Seeder", "Cooling Widget"; activate "Seeder [Cooling Widget]", "Sugar"; activate "Seeder [Cooling Widget]", "Sugar"; buy "Crusher", "Seeder* [Cooling Widget]"; demolish "Crusher"; buy "Crusher", "Greenhouse* (Wheat)"; demolish "Crusher"; buy "Crusher", "Greenhouse* (Corn)"; demolish "Crusher"; buy "Crusher", "Greenhouse* (Corn)"; demolish "Crusher"; buy "Greenhouse", "Sugar"; activate "Greenhouse", "Sugar"; buy "Compactor", ["Wheat", "Wheat"]; demolish "Compactor"; sub box_generation_loop { buy "Harvester"; activate "Harvester"; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Compactor", ["Sugar Seed"]; spend "Corn"; buy "Harvester"; activate "Harvester"; buy "Crusher", "Harvester*"; demolish "Crusher"; activate "Compactor", ["Wheat", "Wheat", "Corn", "Sugar", "Sugar", "Corn Seed", "Corn Seed", "Wheat Seed", "Wheat Seed", "Sugar Seed"]; buy "Crusher", "Compactor*"; demolish "Crusher"; buy "Press"; activate "Press", 5; buy "Crusher", "Press*"; demolish "Crusher"; buy "Press"; activate "Press", 5; buy "Crusher", "Press*"; demolish "Crusher"; } print_comment_wikitext "Box generation loop starts here."; box_generation_loop; print_comment_wikitext "I'm now back where I was at the start of the box generation loop, with 6 fewer Cogs, 9 less Energy, one more 10-box and 2 more 5-boxes. I can afford to do the loop again:"; box_generation_loop; my $extra_box_loop_iterations = 5; print_comment_wikitext "This loop could be repeated until I run out of Cogs or Energy. I do it $extra_box_loop_iterations more times (these loop iterations have been elided)."; { local $silent = 1; box_generation_loop for 1..$extra_box_loop_iterations; } print_assets "State after another $extra_box_loop_iterations box generation loop iterations"; buy "Prototyper", 5; attach "Prototyper", "Cooling Widget"; activate "Prototyper [Cooling Widget]", 5; activate "Prototyper [Cooling Widget]", 5; buy "Crusher", "Prototyper* [Cooling Widget]"; demolish "Crusher"; buy "Prototyper", 5 for 1..3; attach "Prototyper", "Cooling Widget" for 1..3; activate "Prototyper [Cooling Widget]", 5; activate "Prototyper [Cooling Widget]", 10 for 1..4; buy "Crusher", "Prototyper* [Cooling Widget]"; demolish "Crusher"; buy "Prototyper", 10; activate "Prototyper [Cooling Widget]", 10; buy "Crusher", "Prototyper* [Cooling Widget]" for 1..2; demolish "Crusher" for 1..2; buy "Glue Gun", [5, 5]; activate "Glue Gun", [5, 10]; activate "Prototyper", 15; buy "Crusher", "Prototyper*"; buy "Crusher", "Glue Gun*"; demolish "Crusher" for 1..2; sub make_10_things { buy "Harvester"; activate "Harvester"; buy "Crusher", "Harvester*"; demolish "Crusher"; buy "Mill"; attach "Mill", "Cooling Widget"; activate "Mill [Cooling Widget]"; buy "Baker"; activate "Baker"; buy "Crusher", "Baker*"; demolish "Crusher"; buy "Sugarcuber"; activate "Sugarcuber"; buy "Crusher", "Sugarcuber*"; demolish "Crusher"; buy "Harvester"; attach "Harvester", "Cooling Widget"; activate "Harvester [Cooling Widget]", undef, ["Wheat", "Corn", "Sugar", "Wheat Seed"]; activate "Mill [Cooling Widget]"; buy "Crusher", "Mill* [Cooling Widget]"; demolish "Crusher"; spend "Corn" for 1..2; buy "Compactor", ["Sugar", "Corn Seed", "Sugar Seed", "Wheat Seed", "Wheat Seed"]; demolish "Compactor"; activate "Harvester [Cooling Widget]"; buy "Printer", 5; activate "Printer", 5, ["Paper"]; buy "Crusher", "Printer*"; buy "Crusher", "Harvester* [Cooling Widget]"; demolish "Crusher" for 1..2; } my @ten_things = ("Bread", "Corn", "Corn Seed", "Flour", "Paper", "Sugar", "Sugar Seed", "Sugarcube", "Wheat", "Wheat Seed"); make_10_things; buy_tech "Chime", [@ten_things]; make_10_things; buy "Press"; activate "Press", 5; buy "Crusher", "Press*"; demolish "Crusher"; buy "Press"; activate "Press", 5; buy "Crusher", "Press*"; demolish "Crusher"; buy "Chime", [@ten_things]; make_10_things; print_comment_wikitext "$action_count dynastic actions performed this cycle. One more, and I can achieve victory."; print "|}"; activate "Chime", [@ten_things];