Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seeded crawl #968

Merged
merged 28 commits into from Jan 28, 2019

Conversation

Projects
None yet
1 participant
@rawlins
Copy link
Member

rawlins commented Jan 27, 2019

This branch implements improvements to seeding behavior, as well as optional dungeon pregeneration for (hopefully) stable seed->dungeon mappings.

Highlights:

  • Separate RNG states for dungeon generation, one per branch, so that gameplay RNG is completely distinct. This means that even without a fixed dungeon generation order, D:1 will be the same given a seed. RAII interface for selecting an rng.
  • Improvements to make seeding more stable cross-platform (tested on linux vs os x, but not yet on windows): before, vault choice was partly determined by how OS calls provided the .des file order, now, it should be stable (as long as the vault list is the same).
  • Pregeneration, based more-or-less on belrogue/crawl@master...belrogue:seeded_crawl. However, this is implemented in a way that should generate levels cleanly, without various side-effects of the player entering them, and doesn't rely on wizmode calls. Also, there is a UI element that indicates to the user that generation is happening. (Not yet implemented for webtiles.)

Most pregen/seeding features are disabled or hidden for online play; separate game modes for that will come later. (You can see the seed of a dead character, though.)

rawlins added some commits Jan 22, 2019

Improve behavior of seeding
Two main changes in this commit:
1. add additional per-branch RNGs with separate state, so that level gen
randomization doesn't interact with gameplay RNG, and levels interact
less with each other.
2. save RNG state with the player, and restore it on reload, so that the
choice of seed is not just per game session.

This is not a fully seeded crawl in the sense that most people would
want: level generation out-of-order within a branch will still advance
that branch's RNG, and dependencies on order (e.g. uniques, vaults) are
still present. However, it is much closer, in that if levels are loaded
in the same order, the same game seed *should* guarantee the same
dungeon with a few minor caveats (things like Boris, pan lords). For
some discussion of the issues, see:
https://crawl.develz.org/tavern/viewtopic.php?p=306334

Right now seeds are 32 bits, and the seed initializes gameplay rng too,
which may or may not be desireable. Every game is seeded (which is
rather different than the old behavior - games not explicitly seeded
used a pseudorandom 128 bit seed to initialize the rng states.)

Per-branch RNG is perhaps overkill, but relatively simple.
Up the seed size to uint64_t
These is probably overkill, but it's easier to do this now than later.
Move seed / version history out of ?V title
This works less well as this string gets longer.
Use dungeon RNG for initial dungeon setup
Missed this in a previous commit.
Implement dungeon pregeneration (bel)
In order to improve deterministic dungeons (given a seed), allow
pregenerating the entire dungeon in a stable order. This is originally
based on an implementation by bel, that can be found at:
    belrogue/crawl@master...belrogue:seeded_crawl

That approach relied on the player actually entering each level, which
had a number of weird side-effects, and relied on wizmode code (not
available in all builds). This commit attempts to separate out
generation and entering of a level, where a non-visited level will have
0 turns spent on it. It also simplifies the generation order from bel's
version. To enable this, set the option `pregen_dungeon` to true.
Pregeneration is disabled for DGL builds, for two reasons: I'm assuming
seeded runs won't be a thing any time soon for server play, and
pregeneration is a heavy initial CPU hit that might impact server
performance in any case.

There are still a bunch of UI issues after this commit, and it's not
super well tested.

Other side effects:
 * Added an initialization phase for map markers that can run when a
   level is generated - activation happens when a player enters the
   level.
Allow saving ghosts when a seed is set
This check was intended to prevent test code (e.g. qw runs) from
generating ghosts, but it doesn't make sense any more in the context of
seeded crawl.
Document seeding options better, allow rc file seeds
This change also lets offline players view their game seed, and shows it
in the chardump. Yes, this can be used to "cheat" but no more so than
savescumming (in fact, less-so), and based on experience with brogue and
seeded games, this feature will work best if players can view it. It is
impossible to access seeds in online games, and I didn't even put it in
the crash dump on the grounds that this would be a pretty big
information leak - to see a seed for an online game, one would need to
get a save backup and load it offline.
Add some spare RNGs before the branch ones
Given the save compat implications, it would be a lot less annoying to
allocate these now than later.
Add a rudimentary progress popup to run during pregeneration
Since this can take up to a minute, it's nice to show the user that
something is happening. This replaces the "hit <space> to ..." interface
from bel's patch. It does not yet have a webtiles implementation, which
is ok because pregeneration is disabled on webtiles.
Use correct cross-platform format specs for uint64_t
since 64bits can be `long long int` or just `long int` depending on
platform.

Also, fix a bunch of warnings.
Don't do pregeneration for sprints, etc.
They are all one level and the pregen code doesn't handle them anyways.
Add a test that prints D:1 vaults for various seeds
This is not a test that is necessarily likely to crash, but more of a
way of detecting changes. (Also, this is the beginnings of one way of
implementing a seed catalog a la brogue.)
Explicitly cast rng constants to uint64_t
I have no idea if this will matter at all, since `unsigned long long` is
apparently >64bit on some systems, this might(?) contribute to more stable behavior
across builds.
More rng diagnostics for travis
Show exact internal rng state, temple vault choice
Use a stable order for loading vault files
It turns out that all the deterministic RNG in the world doesn't help if
you are choosing among things whose initial order is at the mercy of the
file system.

This commit isn't necessarily a full and complete solution to this
issue, more of a minimal first step; it would be nice to have an even
more stable way of getting vault indices.
Make Boris compatible with pregeneration
This (in a kind of ad-hoc way) simple regenerates Boris with a fixed
probability on appropriate levels, as long as he is dead and has been
killed at least once.
@rawlins

This comment has been minimized.

Copy link
Member Author

rawlins commented Jan 27, 2019

Oh, I should note, since this gets asked a lot -- it isn't enough to just have separate RNG states for each branch, or sub-generators, or (insert fancy RNG trick here). The challenge is that vault generation is not at all independent level-to-level, and for example if a vault has already placed, this could cause a later level to veto with completely different results (and that RNG will diverge after that point). So, stable seed to dungeon mappings require that levels be generated in the same order. Without pregeneration enabled, a seed doesn't give a fully deterministic dungeon in case of shafts, entering branches in a different order, etc.

@rawlins

This comment has been minimized.

Copy link
Member Author

rawlins commented Jan 27, 2019

Missing things (not all of which I will do before merging this, or need to be done first imo):

  • implement progress popup for webtiles
  • add a menu option to play a daily seed
  • implement staged pregeneration, so that it happens as-needed when you enter a new branch. This turns out to be technically nightmarish because of the way load_level is written, and I've 95% decided I can't do that yet in this PR.
  • figure out how to allow special online seeded modes
  • sanity check test on windows...
  • seed catalogue
  • automate some kind of tracking of when seed effect changes?

rawlins added some commits Jan 28, 2019

Two sanity-check fixes
Both of these, I think, were leading to memory corruption. In the case
of the tileweb.cc change, if you were to set the number of ui state
channels to 0, and then try to push a ui update to channel 0, this could
lead to memory corruption if your stdlib let it happen. In the case of
the formatted_string change, what's being pushed back is really an
fs_op, and so the constructor needs to be called, in which case it is
really better as an emplace_back; I'm not 100% sure that this would be
bad on all (or any) stdlib implementations (since it might rely on an
implicit copy constructor or something), but I definitely got an lldb
backtrace with a crash inside this call.
Webtiles implementation for the progress bar
Pregen is still disabled in DGL builds, but with this commit it now
works exactly the same as tiles/console.
Actually use a function added earlier
Was added to consolidate debug code with real setup code, but then only
used for debug code.

@rawlins rawlins merged commit 6b46e92 into crawl:master Jan 28, 2019

1 check was pending

continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.