Пример #1
0
 def get(self, game_id, player_id):
     seedlines = []
     lines = paramVal(self, "seed").split(",")
     game = Game.with_id(game_id)
     hist = Cache.getHist(game_id)
     if not hist:
         Cache.setHist(game_id, player_id, [])
     Cache.setPos(game_id, player_id, 189, -210)
     if not game:
         flags = lines[0].split("|")
         mode_opt = [f[5:] for f in flags if f.lower().startswith("mode=")]
         shared_opt = [
             f[7:].split(" ") for f in flags
             if f.lower().startswith("shared=")
         ]
         mode = MultiplayerGameType.mk(mode_opt[0]) if mode_opt else None
         shared = shared_opt[0] if shared_opt else None
         game = Game.new(_mode=mode, _shared=shared, id=game_id)
         game.put()
     else:
         game.sanity_check()  # cheap if game is short!
     for l in lines[1:]:
         line = l.split("|")
         if len(line) < 3:
             log.error("malformed seed line %s, skipping" % l)
         else:
             seedlines.append("%s:%s" %
                              (line[0], Pickup.name(line[1], line[2])))
     player = game.player(player_id)
     player.seed = "\n".join(seedlines)
     player.put()
     self.response.headers['Content-Type'] = 'text/plain'
     self.response.status = 200
     self.response.out.write("ok")
Пример #2
0
 def from_json(json):
     opts = MultiplayerOptions()
     opts.enabled = json.get("players", 1) > 1
     opts.teams = json.get("teams", {})
     if opts.enabled:
         opts.mode = MultiplayerGameType(json.get("coopGameMode", "None"))
         opts.cloned = json.get("coopGenMode") != "disjoint"
         if opts.cloned:
             opts.teams = {1: range(1, json.get("players", 1) + 1)}
             opts.dedup = bool(json.get("dedupShared", False))
         opts.hints = bool(opts.cloned and json.get("syncHints"))
         opts.shared = enums_from_strlist(ShareType,
                                          json.get("syncShared", []))
     return opts
Пример #3
0
    def from_url(qparams):
        opts = MultiplayerOptions()
        opts.enabled = int(qparams.get("players", 1)) > 1
        if opts.enabled:
            opts.mode = MultiplayerGameType(qparams.get("sync_mode", "None"))
            opts.cloned = qparams.get("sync_gen") != "disjoint"
            opts.hints = bool(opts.cloned and qparams.get("sync_hints"))
            opts.shared = enums_from_strlist(ShareType, qparams.getall("sync_shared"))

            teamsRaw = qparams.get("teams")
            if teamsRaw and opts.mode == MultiplayerGameType.SHARED and opts.cloned:
                cnt = 1
                teams = {}
                for teamRaw in teamsRaw.split("|"):
                    teams[cnt] = [int(p) for p in teamRaw.split(",")]
                    cnt += 1
                opts.teams = teams
        return opts
Пример #4
0
 def get(self, game_id=101):
     game_id = int(game_id)
     if not debug:
         return redirect("/")
     game = Game.with_id(game_id)
     if game:
         game.clean_up()
     seedlines = []
     seed = "mode=Shared|shared=Keys+Skills+Teleporters,-280256|EC|1|Glades,-1680104|EX|100|Grove,-12320248|EX|100|Forlorn,-10440008|EX|100|Misty,799776|EV|5|Glades,-120208|EC|1|Glades,1519708|SH|@Skill For Player 1@|Blackroot,1799708|KS|1|Blackroot,1959768|RB|9|Blackroot,-1560272|KS|1|Glades,-600244|EX|46|Glades,-3160308|HC|1|Glades,-2840236|EX|15|Glades,-3360288|MS|1|Glades,-2480208|EX|6|Glades,-2400212|AC|1|Glades,-1840228|HC|1|Glades,919772|KS|1|Glades,-2200184|KS|1|Glades,-1800156|KS|1|Glades,24|KS|1|Mapstone,2919744|KS|1|Blackroot,-1840196|SK|2|Glades,-800192|AC|1|Glades,-2080116|SK|14|Valley,-560160|EX|32|Grove,1479880|AC|1|Grove,599844|KS|1|Grove,2999904|RB|1|Grove,6999916|KS|1|Swamp,6159900|HC|1|Swamp,3639880|EX|27|Grove,5119584|MS|1|Grotto,6199596|MS|1|Grotto,5719620|HC|1|Grotto,5879616|KS|1|Grotto,6279880|KS|1|Swamp,5119900|EX|67|Swamp,39804|EX|9|Glades,28|EV|5|Mapstone,7839588|EX|51|Grotto,2999808|EC|1|Grove,3039696|SK|50|Blackroot,3119768|EX|33|Blackroot,-2200148|HC|1|Glades,-2240084|EX|53|Valley,4199828|EX|15|Grotto,32|AC|1|Mapstone,3439744|EX|44|Blackroot,-160096|EC|1|Grove,4239780|SK|5|Grotto,-480168|RB|13|Glades,-1560188|KS|1|Glades,-2160176|EX|50|Glades,3319936|KS|1|Grove,4759860|KS|1|Grotto,4319892|EX|82|Grotto,3639888|EC|1|Grove,5799932|EX|114|Swamp,4479832|EX|77|Grotto,5439640|EC|1|Grotto,5639752|EX|56|Grotto,0|EX|62|Grotto,4039612|AC|1|Grotto,3919624|EX|27|Grotto,4959628|EV|5|Grotto,4639628|EX|97|Grotto,4479568|EC|1|Grotto,7559600|EX|30|Grotto,3919688|EX|10|Blackroot,5399780|EX|32|Grotto,5119556|MS|1|Grotto,4439632|EX|40|Grotto,4359656|EX|52|Grotto,4919600|EX|97|Grotto,-1800088|EX|66|Valley,639888|EX|97|Grove,36|EV|5|Mapstone,2559800|EX|83|Glades,-2480280|EX|44|Glades,3199820|EC|1|Grove,1719892|RB|15|Grove,2599880|HC|1|Grove,4079964|EX|30|Swamp,4999892|KS|1|Swamp,5399808|KS|1|Grotto,5519856|EV|5|Grotto,3399820|EX|63|Grove,3279644|MS|1|Grotto,7199904|EV|5|Swamp,8599904|RB|0|Swamp,40|EX|99|Mapstone,799804|EX|22|Glades,6359836|EX|103|Swamp,4479704|EX|82|Grotto,5200140|AC|1|Ginso,5280264|KS|1|Ginso,5080304|MS|1|Ginso,5280296|EX|175|Ginso,5400100|EX|92|Ginso,6639952|KS|1|Swamp,2719900|EV|5|Grove,5320328|AC|1|Ginso,5320488|AC|1|Ginso,5080496|KS|1|Ginso,5400276|EC|1|Ginso,2759624|EV|5|Blackroot,959960|AC|1|Grove,6399872|EX|156|Swamp,4319860|EX|40|Grotto,4319676|AC|1|Blackroot,7679852|RB|0|Swamp,5359824|EX|148|Grotto,8839900|KS|1|Swamp,5160384|EX|118|Ginso,5280404|EX|153|Ginso,5360432|KS|1|Ginso,3879576|AC|1|Blackroot,3359580|KS|1|Blackroot,719620|KS|1|Blackroot,1759964|EX|125|Grove,2239640|AC|1|Blackroot,1240020|HC|1|Grove,559720|KS|1|Glades,39756|EX|73|Glades,-400240|RB|0|Glades,-3559936|EX|49|Valley,-4199936|HC|1|Valley,-3600088|AC|1|Valley,1839836|KS|1|Grove,3519820|KS|1|Grove,5919864|KS|1|Swamp,4199724|EX|171|Grotto,3559792|KS|1|Grotto,3359784|AC|1|Grove,-3200164|EX|155|Valley,3959588|KS|1|Grotto,7599824|KS|1|Swamp,6839792|EX|183|Swamp,7959788|HC|1|Swamp,8719856|EX|61|Swamp,4599508|KS|1|Blackroot,3039472|EV|5|Blackroot,5239456|EC|1|Blackroot,-4600020|MS|1|Valley,-5479948|EX|121|Sorrow,-6800032|KS|1|Misty,-8240012|EX|265|Misty,-2919980|AC|1|Valley,-5719844|EX|62|Sorrow,-5119796|EX|267|Sorrow,-4879680|EX|35|Sorrow,-5039728|RB|1|Sorrow,-5159700|MS|1|Sorrow,-5959772|KS|1|Sorrow,-9799980|EX|86|Misty,-10760004|RB|11|Misty,-10120036|EX|48|Misty,-10759968|HC|1|Misty,-4600188|EX|147|Valley,-4160080|EX|25|Valley,-4680068|RB|6|Valley,-3520100|KS|1|Valley,-5640092|EX|152|Valley,-6119704|EX|1|Sorrow,-4359680|EC|1|Sorrow,-8400124|EX|315|Misty,-7960144|EX|94|Misty,-9120036|EX|315|Misty,-7680144|AC|1|Misty,-11040068|AC|1|Misty,1720000|EC|1|Grove,2519668|EX|253|Blackroot,4560564|EC|1|Ginso,-6719712|EX|178|Sorrow,-6079672|EC|1|Sorrow,-6119656|RB|1|Sorrow,-6039640|EX|140|Sorrow,-6159632|EX|297|Sorrow,-6279608|EX|14|Sorrow,8|EX|9|Misty,44|AC|1|Mapstone,48|EV|5|Mapstone,-7040392|EX|62|Forlorn,-8440352|KS|1|Forlorn,-8920328|MS|1|Forlorn,-8880252|EX|256|Forlorn,-8720256|EX|154|Forlorn,5320660|EX|291|Ginso,5360732|AC|1|Ginso,5320824|AC|1|Ginso,5160864|KS|1|Ginso,4|EX|135|Ginso,6080608|AC|1|Ginso,-6799732|AC|1|Sorrow,-6319752|AC|1|Sorrow,-8160268|AC|1|Forlorn,-5160280|AC|1|Valley,-5400236|EX|236|Valley,-10839992|EX|284|Misty,7639816|AC|1|Swamp,-4559584|RB|6|Sorrow,-4159572|RB|13|Sorrow,-5479592|EX|382|Sorrow,-5919556|KS|1|Sorrow,-6280316|EV|5|Forlorn,12|EX|277|Forlorn,52|MS|1|Mapstone,1920384|KS|1|Horu,1480360|MS|1|Horu,2480400|EX|291|Horu,-6080316|AC|1|Forlorn,1880164|RB|12|Horu,2520192|AC|1|Horu,1600136|KS|1|Horu,-1919808|AC|1|Horu,-319852|AC|1|Horu,120164|EX|128|Horu,1280164|EX|115|Horu,960128|HC|1|Horu,3160244|EX|235|Horu,20|EC|1|Horu,1040112|AC|1|Horu,-8600356|AC|1|Forlorn,-6959592|EX|15|Sorrow,-6479528|HC|1|Sorrow,-4799416|EX|382|Sorrow,4680612|EV|5|Ginso,56|EX|322|Mapstone,-5159576|AC|1|Sorrow,16|EV|5|Sorrow,5040476|RB|21|Ginso,4559492|RB|19|Blackroot,399844|RB|19|Grove,-1680140|EV|5|Glades,9119928|EV|5|Swamp,2079568|EV|1|Blackroot,3279920|RB|17|Grove,-4600256|RB|21|Valley,-4440152|RB|21|Valley,919908|RB|17|Grove,1599920|RB|17|Grove,-11880100|RB|21|Misty,-5400104|EV|5|Valley,-6720040|RB|19|Misty,5039560|RB|17|Grotto,5280500|EV|5|Ginso,5160336|EV|5|Ginso"
     lines = seed.split(",")
     flags = lines[0].split("|")
     mode_opt = [f[5:] for f in flags if f.lower().startswith("mode=")]
     shared_opt = [
         f[7:].split("+") for f in flags if f.lower().startswith("shared=")
     ]
     mode = MultiplayerGameType(mode_opt[0]) if mode_opt else None
     shared = shared_opt[0] if shared_opt else None
     for l in lines[1:]:
         line = l.split("|")
         if len(line) < 3:
             log.error("malformed seed line %s, skipping" % l)
         else:
             seedlines.append("%s:%s" %
                              (line[0], Pickup.name(line[1], line[2])))
     game = Game.new(_mode=mode, _shared=shared, id=game_id)
     game.put()
     for player_id in [1, 2, 3]:
         game = Game.with_id(game_id)
         hist = Cache.getHist(game_id)
         if not hist:
             Cache.setHist(game_id, player_id, [])
         Cache.setPos(game_id, player_id, 189, -210)
         player = game.player(player_id)
         player.seed = "\n".join(seedlines)
         player.put()
     url = uri_for("map-render", game_id=game_id, from_test=1)
     return redirect(url)
Пример #5
0
 def get_mode(self):
     return MultiplayerGameType.mk(
         self.str_mode) or MultiplayerGameType.SIMUSOLO
Пример #6
0
    def get_mode(self): return MultiplayerGameType.from_ndb(self.ndb_mode)

    def set_mode(self, mode):         self.ndb_mode = mode.to_ndb()
    def from_cli(self):
        parser = argparse.ArgumentParser()
        parser.add_argument("--output-dir",
                            help="directory to put the seeds in",
                            type=str,
                            default=".")
        parser.add_argument(
            "--preset",
            help="Choose a preset group of paths for the generator to use")
        parser.add_argument(
            "--custom-logic",
            help=
            "Customize paths that the generator will use, comma-separated: %s"
            % ", ".join(vals(LogicPath)))
        parser.add_argument("--seed",
                            help="Seed value (default 'test')",
                            type=str,
                            default="test")
        parser.add_argument(
            "--keymode",
            help=
            """Changes how the dungeon keys (The Water Vein, Gumon Seal, and Sunstone) are handled:
        Default: The dungeon keys are placed without any special consideration.
        Clues: For each 3 trees visited, the zone of a random dungeon key will be revealed
        Shards: The dungeon keys will be awarded after 3/5 shards are found
        LimitKeys: The Water Vein, Gumon Seal, and Sunstone will only appear at skill trees or event sources
        Free: The dungeon keys are given to the player upon picking up the first Energy Cell.
        """,
            type=str)
        # variations
        parser.add_argument("--hard",
                            help="Enable hard mode",
                            action="store_true")
        parser.add_argument("--ohko",
                            help="Enable one-hit-ko mode",
                            action="store_true")
        parser.add_argument("--zeroxp",
                            help="Enable 0xp mode",
                            action="store_true")
        parser.add_argument(
            "--starved",
            help=
            "Reduces the rate at which skills will appear when not required to advance",
            action="store_true")
        parser.add_argument(
            "--tp-starved",
            help=
            "Reduces the rate at which teleporters will appear early game when not required to advance",
            action="store_true")
        parser.add_argument(
            "--wall-starved",
            help=
            "Reduces the rate at which WallJump and Climb will appear early game when not required to advance",
            action="store_true")
        parser.add_argument(
            "--non-progressive-mapstones",
            help=
            "Map Stones will retain their behaviour from before v1.2, having their own unique drops",
            action="store_true")
        parser.add_argument(
            "--force-trees",
            help=
            "Prevent Ori from entering the final escape room until all skill trees have been visited",
            action="store_true")
        parser.add_argument(
            "--force-mapstones",
            help=
            "Prevent Ori from entering the final escape room until all mapstone altars have been activated",
            action="store_true")
        parser.add_argument("--entrance",
                            help="Randomize entrances",
                            action="store_true")
        parser.add_argument("--closed-dungeons",
                            help="deactivate open mode within dungeons",
                            action="store_true")
        parser.add_argument("--open-world",
                            help="Activate open mode on the world map",
                            action="store_true")
        parser.add_argument(
            "--bonus-pickups",
            help=
            "Adds some extra bonus pickups not balanced for competitive play",
            action="store_true")
        parser.add_argument(
            "--easy",
            help=
            "Add an extra copy of double jump, bash, stomp, glide, charge jump, dash, grenade, water, and wind",
            action="store_true")
        parser.add_argument(
            "--strict-mapstones",
            help=
            "Require a mapstone to be placed when a map monument becomes accessible",
            action="store_true")
        parser.add_argument(
            "--world-tour",
            help=
            "Prevent Ori from entering the final escape until collecting one relic from each of the zones in the world. Recommended default: 8",
            type=int)
        parser.add_argument(
            "--warmth-frags",
            help=
            "Prevent Ori from entering the final escape until collecting some number of warmth fragments. Recommended default: 40",
            type=int)

        # misc
        parser.add_argument(
            "--verbose-paths",
            help="print every logic path in the flagline for debug purposes",
            action="store_true")
        parser.add_argument("--exp-pool",
                            help="Size of the experience pool (default 10000)",
                            type=int,
                            default=10000)
        parser.add_argument(
            "--extra-frags",
            help=
            """Sets the number of extra warmth fragments. Total frag number is still the value passed to --warmth-frags;
        --warmth-frags 40 --extra-frags 10 will place 40 total frags, 30 of which will be required to finish""",
            type=int,
            default=10)
        parser.add_argument(
            "--prefer-path-difficulty",
            help=
            "Increase the chances of putting items in more convenient (easy) or less convenient (hard) locations",
            choices=["easy", "hard"])
        parser.add_argument(
            "--balanced",
            help=
            "Reduce the value of newly discovered locations for progression placements",
            action="store_true")
        parser.add_argument(
            "--force-cells",
            help=
            "Force health and energy cells to appear every N pickups, if they don't randomly",
            type=int,
            default=256)
        # anal TODO: IMPL
        parser.add_argument(
            "--analysis",
            help="Report stats on the skill order for all seeds generated",
            action="store_true")
        parser.add_argument(
            "--loc-analysis",
            help="Report stats on where skills are placed over multiple seeds",
            action="store_true")
        parser.add_argument("--count",
                            help="Number of seeds to generate (default 1)",
                            type=int,
                            default=1)
        # sync
        parser.add_argument("--players",
                            help="Player count for paired randomizer",
                            type=int,
                            default=1)
        parser.add_argument(
            "--tracking",
            help="Place a sync ID in a seed for tracking purposes",
            action="store_true")
        parser.add_argument(
            "--sync-id",
            help="Team identifier number for paired randomizer",
            type=int)
        parser.add_argument(
            "--shared-items",
            help=
            "What will be shared by sync, comma-separated: skills,worldevents,misc,teleporters,upgrades",
            default="skills,worldevents")
        parser.add_argument(
            "--share-mode",
            help=
            "How the server will handle shared pickups, one of: shared,swap,split,none",
            default="shared")
        parser.add_argument(
            "--cloned",
            help="Make a split cloned seed instead of seperate seeds",
            action="store_true")
        parser.add_argument(
            "--teams",
            help=
            "Cloned seeds only: define teams. Format: 1|2,3,4|5,6. Each player must appear once",
            type=str)
        parser.add_argument(
            "--hints",
            help=
            "Cloned seeds only: display a hint with the item category on a shared location instead of 'Warmth Returned'",
            action="store_true")
        parser.add_argument(
            "--do-reachability-analysis",
            help=
            "Analyze how many locations are opened by various progression items in various inventory states",
            action="store_true")
        args = parser.parse_args()
        """
        path_diff = property(get_pathdiff, set_pathdiff)
        exp_pool = ndb.IntegerProperty(default=10000)
        balanced = ndb.BooleanProperty(default=True)
        tracking = ndb.BooleanProperty(default=True)
        players = ndb.IntegerProperty(default=1)
        sync = ndb.LocalStructuredProperty(MultiplayerOptions)
        frag_count = ndb.IntegerProperty(default=40)
        frag_extra = ndb.IntegerProperty(default=10)
        cell_freq = ndb.IntegerProperty(default=256)
        """
        self.seed = args.seed
        if args.preset:
            self.logic_paths = presets[args.preset.capitalize()]
        elif args.custom_logic:
            self.logic_paths = enums_from_strlist(LogicPath,
                                                  args.custom_logic.split(","))
        else:
            self.logic_paths = presets["Standard"]
        self.key_mode = KeyMode.mk(args.keymode) or KeyMode.NONE
        # variations (help)
        varMap = {
            "zeroxp": "0XP",
            "non_progressive_mapstones": "NonProgressMapStones",
            "ohko": "OHKO",
            "force_trees": "ForceTrees",
            "starved": "Starved",
            "force_mapstones": "ForceMapStones",
            "entrance": "Entrance",
            "open_world": "OpenWorld",
            "easy": "DoubleSkills",
            "strict_mapstones": "StrictMapstones",
            "warmth_frags": "WarmthFrags",
            "world_tour": "WorldTour",
            "closed_dungeons": "ClosedDungeons",
            "tp_starved": "TPStarved",
            "wall_starved": "WallStarved"
        }
        self.variations = []
        for argName, flagStr in varMap.iteritems():
            if getattr(args, argName, False):
                v = Variation.mk(flagStr)
                if v:
                    self.variations.append(v)
                else:
                    log.warning("Failed to make a Variation from %s" % flagStr)
        if Variation.WORLD_TOUR in self.variations:
            self.relic_count = args.world_tour
        if Variation.WARMTH_FRAGMENTS in self.variations:
            self.frag_count = args.warmth_frags
            self.frag_extra = args.extra_frags
        #misc
        self.exp_pool = args.exp_pool
        if args.prefer_path_difficulty:
            if args.prefer_path_difficulty == "easy":
                self.path_diff = PathDifficulty.EASY
            else:
                self.path_diff = PathDifficulty.HARD
        else:
            self.path_diff = PathDifficulty.NORMAL
        self.balanced = args.balanced or False
        self.cell_freq = args.force_cells
        self.players = args.players
        self.tracking = args.tracking or False
        self.sync = CLIMultiOptions()
        if Variation.EXTRA_BONUS_PICKUPS in self.variations:
            self.pool_preset = "Extra Bonus"
            self.item_pool = {
                "TP|Grove": [1],
                "TP|Swamp": [1],
                "TP|Grotto": [1],
                "TP|Valley": [1],
                "TP|Sorrow": [1],
                "TP|Ginso": [1],
                "TP|Horu": [1],
                "TP|Forlorn": [1],
                "HC|1": [12],
                "EC|1": [14],
                "AC|1": [33],
                "RB|0": [3],
                "RB|1": [3],
                "RB|6": [5],
                "RB|9": [1],
                "RB|10": [1],
                "RB|11": [1],
                "RB|12": [5],
                "RB|13": [3],
                "RB|15": [3],
                "RB|31": [1],
                "RB|32": [1],
                "RB|33": [3],
                "BS|*": [4],
                "WP|*": [4, 8],
            }
        else:
            self.pool_preset = "Standard"
            self.item_pool = {
                "TP|Grove": [1],
                "TP|Swamp": [1],
                "TP|Grotto": [1],
                "TP|Valley": [1],
                "TP|Sorrow": [1],
                "TP|Ginso": [1],
                "TP|Horu": [1],
                "TP|Forlorn": [1],
                "HC|1": [12],
                "EC|1": [14],
                "AC|1": [33],
                "RB|0": [3],
                "RB|1": [3],
                "RB|6": [3],
                "RB|9": [1],
                "RB|10": [1],
                "RB|11": [1],
                "RB|12": [1],
                "RB|13": [3],
                "RB|15": [3],
            }

        if self.players > 1 or self.tracking:
            self.sync_id = args.sync_id or int(time.time() * 1000 % 1073741824)
        if self.players > 1:
            self.sync.enabled = True
            self.sync.mode = MultiplayerGameType.mk(
                args.share_mode) or MultiplayerGameType.SIMUSOLO
            self.sync.shared = enums_from_strlist(ShareType,
                                                  args.shared_items.split(","))
            self.sync.cloned = args.cloned or False
            if self.sync.cloned:
                self.sync.hints = args.hints or False
                if args.teams:
                    cnt = 1
                    self.sync.teams = {}
                    for team in args.teams.split("|"):
                        self.sync.teams[cnt] = [
                            int(p) for p in team.split(",")
                        ]
                        cnt += 1

        # todo: respect these LMAO
        self.do_analysis = args.analysis
        self.do_loc_analysis = args.loc_analysis
        self.repeat_count = args.count

        base_seed = self.seed[:]
        if self.do_analysis:
            key_items = [
                "WallJump", "ChargeFlame", "DoubleJump", "Bash", "Stomp",
                "Glide", "Climb", "ChargeJump", "Dash", "Grenade", "GinsoKey",
                "ForlornKey", "HoruKey", "Water", "Wind", "TPForlorn",
                "TPGrotto", "TPSorrow", "TPGrove", "TPSwamp", "TPValley",
                "TPGinso", "TPHoru"
            ]
            info_by_group = defaultdict(defaultgroup)

        if self.do_loc_analysis:
            self.locationAnalysis = {}
            self.itemsToAnalyze = {
                "WallJump": 0,
                "ChargeFlame": 0,
                "DoubleJump": 0,
                "Bash": 0,
                "Stomp": 0,
                "Glide": 0,
                "Climb": 0,
                "ChargeJump": 0,
                "Dash": 0,
                "Grenade": 0,
                "GinsoKey": 0,
                "ForlornKey": 0,
                "HoruKey": 0,
                "Water": 0,
                "Wind": 0,
                "WaterVeinShard": 0,
                "GumonSealShard": 0,
                "SunstoneShard": 0,
                "TPForlorn": 0,
                "TPGrotto": 0,
                "TPSorrow": 0,
                "TPGrove": 0,
                "TPSwamp": 0,
                "TPValley": 0,
                "TPGinso": 0,
                "TPHoru": 0,
                "Relic": 0
            }
            for i in range(1, 10):
                self.locationAnalysis["MapStone " +
                                      str(i)] = self.itemsToAnalyze.copy()
                self.locationAnalysis["MapStone " +
                                      str(i)]["Zone"] = "MapStone"

        for count in range(0, self.repeat_count):

            if self.repeat_count > 1:
                self.seed = "%s_%s" % (base_seed, count)

            if self.do_loc_analysis:
                print(self.seed)

            sg = SeedGenerator()

            if args.do_reachability_analysis:
                sg.do_reachability_analysis(self)
                return

            raw = sg.setSeedAndPlaceItems(self, preplaced={})
            seeds = []
            spoilers = []
            if not raw:
                log.error("Couldn't build seed!")
                if self.do_loc_analysis:
                    continue
                return
            player = 0
            for player_raw in raw:
                player += 1
                seed, spoiler = tuple(player_raw)
                if self.tracking:
                    seed = "Sync%s.%s," % (self.sync_id, player) + seed
                seedfile = "randomizer_%s.dat" % player
                spoilerfile = "spoiler_%s.txt" % player
                if self.players == 1:
                    seedfile = "randomizer" + str(count) + ".dat"
                    spoilerfile = "spoiler" + str(count) + ".txt"

                if not self.do_analysis and not self.do_loc_analysis:
                    with open(args.output_dir + "/" + seedfile, 'w') as f:
                        f.write(seed)
                    with open(args.output_dir + "/" + spoilerfile, 'w') as f:
                        f.write(spoiler)
                if self.do_analysis:
                    i = 0
                    for spoiler_line in spoiler.split("\n"):
                        fl = first_line_pattern.match(spoiler_line)
                        if fl:
                            raw_group, raw_locs = fl.group(1, 2)
                            i = int(raw_group)
                            info_by_group[i]["seeds"] += 1
                            info_by_group[i]["locs"] += raw_locs.count(",") + 1
                            continue
                        fp = forced_pickup_pattern.match(spoiler_line)
                        if fp:
                            info_by_group[i]["force"] += 1
                            for raw in fp.group(1).split(","):
                                info_by_group[i]["forced"][raw.strip(
                                    " '")] += 1
                            continue
                        nl = normal_line_pattern.match(spoiler_line)
                        if nl:
                            item = nl.group(1)
                            if item in key_items:
                                info_by_group[i]["items"][item] += 1

        if self.do_analysis:
            #            output = open("analysis.csv", 'w')
            #            output.write("Location,Zone,WallJump,ChargeFlame,DoubleJump,Bash,Stomp,Glide,Climb,ChargeJump,Dash,Grenade,GinsoKey,ForlornKey,HoruKey,Water,Wind,WaterVeinShard,GumonSealShard,SunstoneShard,TPGrove,TPGrotto,TPSwamp,TPValley,TPSorrow,TPGinso,TPForlorn,TPHoru,Relic\n")
            for i, group in info_by_group.items():
                seeds = float(group["seeds"])
                print "%d (%d): " % (i, int(seeds))
                print "\tkey items: [",
                for item, count in group["items"].items():
                    print '%s: %02.2f%%,' % (item, 100 * float(count) / seeds),
                print "]\n\tforced: [",
                for item, count in group["forced"].items():
                    print '%s: %02.2f%%,' % (item, 100 * float(count) /
                                             float(group["force"])),
                print "]\n\taverage locs", float(group['locs']) / seeds
            with open("anal.pickle", 'w') as out_file:
                pickle.dump(info_by_group, out_file)
            with open("analysis.csv", 'w') as out_file:
                out_file.write(
                    "Group,Seeds,Forced,Locs,WallJump,ChargeFlame,DoubleJump,Bash,Stomp,Glide,Climb,ChargeJump,Dash,Grenade,GinsoKey,ForlornKey,HoruKey,Water,Wind,TPGrove,TPGrotto,TPSwamp,TPValley,TPSorrow,TPGinso,TPForlorn,TPHoru,WallJump,ChargeFlame,DoubleJump,Bash,Stomp,Glide,Climb,ChargeJump,Dash,Grenade,GinsoKey,ForlornKey,HoruKey,Water,Wind,TPGrove,TPGrotto,TPSwamp,TPValley,TPSorrow,TPGinso,TPForlorn,TPHoru,KS,EC,HC,MS,AC\n"
                )
                for i, group in info_by_group.items():
                    seeds = float(group["seeds"])
                    line = [
                        i,
                        int(seeds),
                        int(group["force"]),
                        "%02.2f" % (float(group["locs"]) / seeds)
                    ]
                    for key_item in [
                            "WallJump", "ChargeFlame", "DoubleJump", "Bash",
                            "Stomp", "Glide", "Climb", "ChargeJump", "Dash",
                            "Grenade", "GinsoKey", "ForlornKey", "HoruKey",
                            "Water", "Wind", "TPGrove", "TPGrotto", "TPSwamp",
                            "TPValley", "TPSorrow", "TPGinso", "TPForlorn",
                            "TPHoru"
                    ]:
                        line.append(
                            "%02.2f%%" %
                            (100 * float(group["items"][key_item]) / seeds))
                    for forced_prog in [
                            "WallJump", "ChargeFlame", "DoubleJump", "Bash",
                            "Stomp", "Glide", "Climb", "ChargeJump", "Dash",
                            "Grenade", "GinsoKey", "ForlornKey", "HoruKey",
                            "Water", "Wind", "TPGrove", "TPGrotto", "TPSwamp",
                            "TPValley", "TPSorrow", "TPGinso", "TPForlorn",
                            "TPHoru", "KS", "EC", "HC", "MS", "AC"
                    ]:
                        line.append(
                            "%02.2f%%" %
                            (100 * float(group["forced"][forced_prog]) /
                             float(group["force"])
                             if group["forced"][forced_prog] else 0))
                    out_file.write(",".join([str(x) for x in line]) + '\n')
        if self.do_loc_analysis:
            output = open("analysis.csv", 'w')
            output.write(
                "Group,WallJump,ChargeFlame,DoubleJump,Bash,Stomp,Glide,Climb,ChargeJump,Dash,Grenade,Water,Wind,WaterVeinShard,GumonSealShard,SunstoneShard,TPGrove,TPGrotto,TPSwamp,TPValley,TPSorrow,TPGinso,TPForlorn,TPHoru,\n"
            )
            for key in self.locationAnalysis.keys():
                line = key + ","
                line += str(self.locationAnalysis[key]["Zone"]) + ","
                line += str(self.locationAnalysis[key]["WallJump"]) + ","
                line += str(self.locationAnalysis[key]["ChargeFlame"]) + ","
                line += str(self.locationAnalysis[key]["DoubleJump"]) + ","
                line += str(self.locationAnalysis[key]["Bash"]) + ","
                line += str(self.locationAnalysis[key]["Stomp"]) + ","
                line += str(self.locationAnalysis[key]["Glide"]) + ","
                line += str(self.locationAnalysis[key]["Climb"]) + ","
                line += str(self.locationAnalysis[key]["ChargeJump"]) + ","
                line += str(self.locationAnalysis[key]["Dash"]) + ","
                line += str(self.locationAnalysis[key]["Grenade"]) + ","
                line += str(self.locationAnalysis[key]["GinsoKey"]) + ","
                line += str(self.locationAnalysis[key]["ForlornKey"]) + ","
                line += str(self.locationAnalysis[key]["HoruKey"]) + ","
                line += str(self.locationAnalysis[key]["Water"]) + ","
                line += str(self.locationAnalysis[key]["Wind"]) + ","
                line += str(self.locationAnalysis[key]["WaterVeinShard"]) + ","
                line += str(self.locationAnalysis[key]["GumonSealShard"]) + ","
                line += str(self.locationAnalysis[key]["SunstoneShard"]) + ","
                line += str(self.locationAnalysis[key]["TPGrove"]) + ","
                line += str(self.locationAnalysis[key]["TPGrotto"]) + ","
                line += str(self.locationAnalysis[key]["TPSwamp"]) + ","
                line += str(self.locationAnalysis[key]["TPValley"]) + ","
                line += str(self.locationAnalysis[key]["TPSorrow"]) + ","
                line += str(self.locationAnalysis[key]["TPGinso"]) + ","
                line += str(self.locationAnalysis[key]["TPForlorn"]) + ","
                line += str(self.locationAnalysis[key]["TPHoru"]) + ","
                line += str(self.locationAnalysis[key]["Relic"])

                output.write(line + "\n")