#A Storyteller Drinks Java

141 messages · Page 1 of 1 (latest)

misty sky
#

I got bored, so I'm trying to write a Java program that can reasonably recreate most of the shenanigans that happen in a BotC game. I'm putting it here because [it's clogging the text game general chat](#text-game-general message) and people seem interested in it, apparently? I have some funny ideas for how to use this program for an eventual text game I want to storytell, maybe in August when my birthday rolls around. We'll see.

#

@keen spade Would you be willing to help me some more with this, or...
Something I'm wondering is how I make roles interact with ST consults, if at all.

#

Since all interactions are effectively forced to go through the Storyteller, it doesn't make that much sense for the role to have a function that just immediately diverts to some ST.prompt(...) function. But then there's the Amnesiac ability which gets information that must fit into some category.

keen spade
#

I can try to assist if needed for specific questions.

rugged remnant
#

Eww... inheritence < delegation

keen spade
#

A bit busy in general IRL and with running a game as is, but can answer questions when they come up

misty sky
#

I was kinda expecting to get booed out for randomly bringing this up, yet it seems like everyone I've talked to has contributed.

coarse finch
#

now that's unfair

#

I'm only a CS minor

#

that said, the specific kind of person that plays a social deduction game via discord text is likely to also be pretty familiar with computers at some level

rugged remnant
#

Tho has summoned the who unspoken (java)

misty sky
#

I think I did the thing.

keen spade
#

I'm a mathematics major, currently a Ph.D student. Have done programming work for my research though.

rugged remnant
misty sky
#

Matron. Pit Hag. Barber.

#

Well, technically Matron moves seats around, but Pit Hag changes the underlying role while not changing the player nor their alignment.

misty sky
rugged remnant
#

I think its a fun idea to have those as interfaces, though problem could be that its "too magical".

Also i think the defaultAlignment is a weird property. Spy misreg only allowed for good, TF and outsiders. While recluse demon evil minion

misty sky
#

Well, it's the starting alignment of the role. It can change for things like Cult Leader, Mezepheles, or Goon.

#

I'm not sure of a better way to handle Misregistration.

rugged remnant
#

"Can misregister as"?

#

Also misregistration could trigger math

#

So maybe the decision needs to be somewhere else

misty sky
#

Maybe the grim as a whole stores the math number.

fossil otter
#

The real prize here is a program that can ST Quantum clocktower, since thats the only game mode with absolutely no ST agency, so a program is a strictly better ST than a human 😉

rugged remnant
#

Well technically the ST does tiebreakers in quantum. Though nothing too bad for a computer

#

Regular Quantum Text Games run by a computer would be dope tho

fossil otter
misty sky
#

Another weird thing: do reminders bind to the player, the role, or to the seat?

#

If the Matron swaps the spent professor away from the Tea lady, for example, then the professor "no ability" token should move, but the "can't die" token should not.

#

But if, say, the Philosopher is Pit Hagged into the Sweetheart, then the Philosopher's "Drunk" token should be removed, but that token can be anywhere on the grim.

#

I'm kinda thinking I put them on both the Role and the player, as a 2-way link.

#

That way, if the Role is removed, then it can tell the other seats to remove the relevant tokens, and if the player is moved, it can tell the role to check if it needs to update who has what tokens.

rugged remnant
#

Possibly a remindertokenholder

misty sky
#

Oh, I see.

#

Since I'd probably end passing the entire grim as a parameter to a "check the reminders" function, I might as well store them in a single location.

potent tusk
#

After reading

#

Suddnely

#

physics books look like mercy

misty sky
#

This seems like a decent final project for a Data Structures course.

#

Or maybe Data management. I'm not taking either course anytime soon, though.

#

Actually, no, at my uni it would probably be CS421 - Principles of Database System Implementation. This is literally it.

#

Once I figure out a foolproof method to store and access data in all the special cases, then implementing all of the roles becomes easy.

#

Which is basically like saying "once I code in all the special cases, then the general case is easy".

keen spade
terse ruin
terse ruin
#

Player should probably have at least the following attributes:

Name
Character
Alignment
Voting History
Nomination History
Dead/Alive State
Sober/Poisoned state

etc ... etc...

fossil otter
# keen spade Where can I see the rules for how Quantum BotC works? I could try working on suc...

Quantum Mechanics professor
Law 1: All players always exist in a superposition of all characters and states in which they could possibly be.
Law 2: Players tell the Storyteller their choices and information each night, which become true unless mechanically impossible. Players learn when they make an impossible choice, and must change that choice. If mutually exclusive events would occur simultaneously, one occurs at random.
Law 3: Players receive specific prompts if and only if their superposition has collapsed to only contain states where they receive that specific prompt.
Law 4: Players appear to die if and only if their superposition has collapsed to only contain states where they have died.
Law 5: Players can nominate and vote without consuming their vote token if and only if they appear alive.
Law 6: The game proceeds in strict night order, but if a player's choice is impossible, they make a new choice for the entire night (ideally submitting multiple possibilities at once, processed in order submitted), so they may submit a choice that will happen before their first attempt would have happened in the night.
Law 7: Starting alignment will be rolled randomly. After that, starting evils (if any) will learn each other and then submit who they learn as demon/minions within that team when they submit N1 actions. Evil starting information happens simultaneously, at the very start of N1.
Law 8: All game rules are always followed, and the setup is always valid.
Law 9: The game continues if possible, ending if and only if its superposition has collapsed to only contain states where it ends.
Law 10: When the game ends, good wins ties within a possible game state (per normal BotC rules; example is executing the demon at final 3). Evil wins ties between possible game states (unique Quantum rule; example is executing the superpositioned Lleech/Pukka at final 3).

midnight sail
fossil otter
#

Gossips are required to be in the form of valid if statements using variables that can be evaluated by the ST

misty sky
#

"It is the opinion of the ST that good is winning"
< Crash >

fossil otter
#

RAW, gossips must be definite statements, so "good is winning" shouldn't be allowed anyway 😉

midnight sail
#

“in the current gamestate, a general would get a ‘good’”

fossil otter
#

thats still indefinite as a general can get whatever the ST subjectively thinks

#

General can get indefinite info but gossip can't

rugged remnant
terse ruin
vernal tangle
misty sky
#

Okay, so I've been thinking about it a bit more, and I think the best method to deal with reminder shenanigans would be to have a Set of "in play" tokens derived from the roles in play.

#

The in play tokens may or may not be on the grim, but only those tokens can be on any given player.

#

Then, I have a list of sets of tokens (List<Set<Reminder>>) indicating which reminders (if any) are on a given player.

#

Or, I can add a Set<Reminder> property to each Seat.

#

So then, if the Fortune Teller picks Sterling and De, I can then check Seats 6 and 9, see that the FortuneTeller.RED_HERRING Token is in Seat 6, and return true.

rugged remnant
#

Why have a List<Set<Reminder>> tho

misty sky
#

I'm realizing that. I'm instead folding it into the Seats' information.

misty sky
#

Not sure how to delineate the tokens. Strings don't have enough functionality.

misty sky
#

I should probably just focus on implementing something like TB. I keep thinking of niche things like how to deal with the Fang Gu "once" reminder if it leaves and re-enters play. Maybe bind a persistent token to the ST?

coarse finch
#

not a bad idea, I've seen people run the Fang Gu "once" token and the Plague Doctor's ST Minion ability in the same way, putting it in the center of the grim

#

making the ST the store of any global traits isn't a bad idea when throwing things together

midnight sail
misty sky
#

A lot of them come for free.

#

Well, the ST is prompted every time a decision involving misinfo comes up.

#

"The Chef should get a 0, but is poisoned. What value will they be shown?"
"The Recluse neighbors the Poisoner. Should that increase the Chef number?"
The hard part at that point is determining how many times the Mathematician should be pinged.

#

There will probably be a config thing that makes pedantically stupid "yes but don't" scenarios be ignored in favor of the obvious conclusion, like the recluse learning who the minions are.

rugged remnant
misty sky
#

That interface would need to be updated every time a new token is added.

#

Wait... uh... enums and interfaces?

rugged remnant
#

You can define an interface as a type and use enums to have only one instance of the drunk reminder token

misty sky
#

I'm not sure how to do it, I don't think.

#

Maybe my Java is just bad.

vernal tangle
#

so tempted to make a c++ version of this thread

#

It’d be harder to make a punny name though

misty sky
#

Storytellers_c_all

vernal tangle
#

A Storyteller Drinks C++fuel

#

like gfuel

misty sky
#

Maybe I could do a thing where I can "summarize" the data on a Seat. Instead of asking for a bunch of properties, I ask the Seat to generate an Ad-Hoc summary of its properties, which the Role requesting the data can deal with.
So something like:

PlayerData {
  trueRole: Recluse
  trueType: Outsider
  trueAlignment: Good
  apparentRoles: [Recluse, Poisoner, Spy, Baron, Scarlet_Woman, Imp]
  apparentTypes: [Outsider, Minion, Demon]
  apparentAlignments: [Good, Evil]
  reminders: [Undertaker -- Executed]
}

Then, the undertaker can see this PlayerData, and the program can prompt the storyteller about whether the Recluse registers as the Recluse, or as one of the evil TB roles.

#

Then, when the Bootlegger's Almanac is ported to this, implementing Huli Jing isn't a nightmare.

#

Anyways, scuffed FT implementation.

misty sky
#

Okay, I've reached a roadblock: Arbitrary class instantiation.

#

Somewhere in memory, there needs to be a list containing all of the Role classes that can be instantiated. But instnatiating is hard, or at the very least not recommended. I can't really hardcode it for extensibility.

#

Because it'd need to be something like a HashMap<String, Class<? extends Role>> where I can then run allRoles.get("FortuneTeller").newInstance(). But it seems... Janky.

#

This would be so easy in Python... this is so scuffed.

misty sky
#

I think this looks cleaner. Thoughts?

tropic dawn
#

idk what’s going on in your PlayerData class, but it seems more intuitive to have a getAlignment() method and have things like recluse/spy override it?

#

rather than mustAppearEvil and canAppearEvil

#

the ST discretion can happen in the recluse/spy classes instead of in the chef’s which saves you rewriting some code

#

ideally an ST sees the call stack when prompted so knows that this is a chef check and not, say, an empath

misty sky
#

The problem is that if, if that happens multiple times for the chef, then the math number may not be updated correctly.

#

The ST will be prompted with context as to what's going on when they are asked for a decision.

misty sky
rugged remnant
tropic dawn
#

but you’ve clearly thought this through more than I have

#

so if I’m spouting nonsense then feel free to ignore :P

#

aaah I see the problem with what I’m proposing - a Player has-a character and has-a alignment and it’s messy for a character to know which player has it (to pull alignment)

misty sky
#

Evil Cult leader, for example.

#

I'm trying to design around every contingency so writing out every role becomes as straightforward as possible.

keen spade
misty sky
#

Although I still feel like my code is dogwater.

vernal tangle
#

forgot that the word for running code is executing so I thought I got clocktower jumpscared

misty sky
covert osprey
#

It would be an ST assist tool

rugged remnant
#

i am working on a tool for quantum

misty sky
#

Gonna be honest, I've hit a major roadblock with roles: how can I easily add all the roles and their abilities, and how can another user easily add new roles?

#

Like, most of the data, such as what type a character is, can be imported via JSON. Once I figure out how.
But how does the night action work? How would you encode, say, a Fortune Teller? Sure, you can add that a FT picks 2 players, learns if either is a demon, and has a persistent player-dependent "red herring" reminder, but how would you tell the program that the player who is the red herring must be good, and must register as the demon to the Fortune teller?

terse ruin
#

What is preventing you from having logic in your program for the abilities?

#

I think for FT for example you can modify your application state to add the player to the red herring variable and then add logic for how the program interacts with that player.

old glen
# misty sky Gonna be honest, I've hit a major roadblock with roles: how can I easily add all...

@misty sky I'd be interested to hear how you fare in general - I am currently doing a 'research' project for a university course looking at using model checking/SMT solving for Uncertainty principle (to build a ST helper tool). I decided to take this route because under the hood, we're just looking at a bunch of temporal logic constraints and ideally the model will be able to give back a minimal core of actions made by players that are logically viable - thus telling the storyteller which person/s they need to randomly select to pick again because their choice is logically impossible.
I've only got 14 weeks within the course to make some sort of implementation and write a report, so we'll see how far I get..

Have you tried simulating a minimal set of characters or are you still building the framework with every possible character in mind?

summer laurel
#

This has piqued my interest. I almost feel as though reminder tokens would need to be a field on all 3 of Seat, Role, and Player. All 3 may need their own unique hasReminder method, but then one of Grimoire or Seat would also have a "global" hasReminder method that checks an aggregated list of all reminders for the relevant Seat, Role, and Player.

public class Grimoire {
    public bool seatHasReminder(Seat seat, Reminder reminder) {
        return seat.hasReminder(reminder) || getPlayer(seat).hasReminder(reminder) || getRole(seat).hasReminder(reminder);
    }
}
#

As for adding new roles, I expect that would always have to be manual. For the FT example, the nightAction method handles the Red Herring misregistration, and the Role class can have a setup method can be used to perform setup actions such as FT Red Herring or WW Townsfolk/Wrong reminder tokens during the setup phase of the game:

public class FortuneTeller extends Role {
    public void setup(Grimoire grim, int playerId) {
        int choiceId = Prompt.getNumber("Pick a player to be the Fortune Teller's Red Herring", 0, grim.playerCount());
        PlayerData data = grim.getSeat(choiceId).data(grim);
        while (data.getAlignment() != Alignment.Good) {
            choiceId = Prompt.getNumber("The Red Herring must be on a Good player", 0, grim.playerCount());
        }
        data.addReminder(RED_HERRING);
    }
}

Excuse my code if it's non-idiomatic or non-compiling 🙂 I'm a C# guy

violet kettle
#

Oh

misty sky
violet kettle
#

Hold up,are we making a whole script by coding it?

#

cool!

misty sky
#

I think I could create a shitty programming language (eg: "ClockScript") that could abstract the logic, but that would take a while, and I don't know much about Programming Language design.

#

So, instead, I'm gonna just hardcode all the logic for the base 3 scripts.

#

It seems that BMR is gonna be the easiest script to implement. TB has misregistration, which is a can of worms, and SnV has the Savant, which... ugh, and the Mathematician.

#

The problem is that BMR has the Minstrel. I'm not sure how to deal with "global" events. Maybe I could add a system where the grim queries all other players to see if they have any influence on a player's info. Then I can compute the barista-correct info, and then prompt the ST to see if they want/need to come up with something different.

misty sky
#

Anyways, my first thing is trying to implement the setup. The problem is that setup modification is annoying.
For example: The drunk. In a 12-player game of TB, there are 2 outsiders and 7 townsfolk. With a drunk, you have to put in 8 townsfolk and 1 outsider, and retroactively make a player drunk. I'm not sure how to represent it.

#

Maybe something like:

setup_modifier: {
  "requires": ["King"], // choirboy
  "jinxed": ["Spy", "Widow"], // Heretic
  "bagged": false // Drunk
  "OUTSIDER": [1, 0, -1], // Sentinel
  "MINION": [1], // Lil' Monsta
  "DEMON": [Infinity], // Riot
}

in the JSON I'm making.

#

Actually... this can't represent "most" for Legion... I could lazily put [5,6,7...], but 5 with 15 is invalid.

old glen
#

That’s a good point.
SMT is a solving engine - can tell you if a world is satisfiable or not - is it logically possible for all constraints to be true.

I am implementing a specific quantum script because my focus is on quantum and a bunch of characters break quantum Clocktower (e.g. savant, pit-hag, spy).

old glen
misty sky
#

This is JSON being parsed by Java. You can't put functions in it.

old glen
misty sky
#

So I put in "HALF" and have it parse that?