Modbot to Help You Run Games: Now With A Votecounter Demo!

This forum is for discussion related to the game.
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #18 (isolation #0) » Tue May 01, 2018 2:55 pm

Post by GreenLiquid »

First off, I wanted to say I'm really excited to see this, and I also want to thank you, Psyche, for encouraging me to start learning Python. I really enjoy the language and I probably wouldn't have tried it if you hadn't suggested it to me like 6 months ago.
In post 9, Psyche wrote:would be too much to ask people to write json files, right? https://cdn-images-1.medium.com/max/160 ... pyFSkw.png
I think JSON is the way to go, not only because it's not terribly difficult to encode data in that format, but also because it'll make life easier if anyone ever wants to hook this module into other modules. And also because mine specifically uses JSON :P
But I think Math is right that a UI is going to be valuable. I imagine it's not too hard to set up a UI that takes in setup inputs and spits out the JSON the vote counter needs.

A couple of things I'd suggest for the setup file, based on what I've set up in my own module:
- It would be a good idea for the setup to have a "mods" list, that way you can account for having co-mods and backup mods and the like. This could help the scraper avoid counting things that look like votes from any mod players.
- For players as well, to deal with hydrae it would be a good idea to have "aliases" or something of the sort.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #24 (isolation #1) » Wed May 02, 2018 3:51 pm

Post by GreenLiquid »

This is already looking pretty cool!
I don't have any knowledge about how to automate operations to the forum, such as posting and whatnot, but the bit I've been working on is intended to be able to store a "game state" which can be modified by passing events to it, like "Player A votes for Player B" and stuff like that. I also have a thing for automatically generating vote counts, the player list, the list of events in the first/second post, and the alive/dead/modkilled lists. I'm in the middle of rewriting stuff so it's not really in a suitable state to use at the moment but I would be happy to share what I have when I'm done in like a week or so. You might be able to scavenge some useful code out of it for the project.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #27 (isolation #2) » Wed May 02, 2018 4:57 pm

Post by GreenLiquid »

For testing, maybe you could use a PT? Can the DonBot access PTs visible to your account when logging in under it?
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #75 (isolation #3) » Thu May 10, 2018 1:17 pm

Post by GreenLiquid »

I think I'm more or less done with my rewrite and I'd like to share what I've got in case you want to mine anything useful out of it or offer suggestions. I've cloned GitHub repositories for work but I've never added to one -- is that something I'm able to do with yours or do I need to create my own?
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #80 (isolation #4) » Thu May 10, 2018 4:59 pm

Post by GreenLiquid »

Alright, I got my files uploaded to a fork and I
think
I did the pull request right -- please let me know if I effed something up!

I had three main objectives for the code I've been working on:

1. Make it as robust as (reasonably) possible, so that mods with fairly bizarre or nonstandard voting rules in their setups can still make use of the tool, provided they're willing to put in the effort writing the config!
2. Make a standardized representation of the state of a game, and allow only clearly-defined operations upon it, called Events, which can be generated from JSON, so that other tools like Psyche's thread crawler can be utilized to create JSON events to be fed directly into the state object.
3. Make a system for turning the game state into posts like vote counts, player lists, role PMs, etc. in a way that is extremely customizable -- as Psyche said, mods have all sorts of ways they like to make their vote counts, so being able to accommodate all of them (including myself!) would probably be appreciated by a lot of users, and all the better if the method of customizing output does not require any Python or code knowledge whatsoever.

So, what exactly do I have so far? Basically, this:

Firstly, the classes and methods needed to create a "GameState" object which holds (ideally) the entire state of a particular game. Nothing is ever deleted out of the GameState: if a vote is no longer active (due to a player unvoting or voting for someone else), it gets its "end" timestamp set to reflect that. Consequently, it's possible to go to the GameState and ask for a vote count accurate to any post in the game, and it should be able to go back and pull a vote count for you. The code I have written in my Main.py currently does essentially that. The 'pie-in-the-sky' use for this would be, if for some reason this code were someday integrated into the site directly, that you could go to any post in a game, click a button, and a vote count as of that post would be generated for you. With a system like that, it might not even be necessary to have the mod post a vote count on every page. :P

Of course, this is all just me setting the bar high so as to encourage myself to make the system as robust as possible. Anyway, what all is included in this module?

The classes under the GameState umbrella would be:
GameState
: The container for all of the game's state data. Pretty straightforward.
Player
: The container for a particular player's data. This represents a 'slot' in the game, not a particular user who has been cast into that slot. A Player contains a list of all its users (needed to represent replacements), and also its role and alignment.
User
: A representation of a Mafiascum user. The "display_name" should be the user's MS username, and "aliases" can be used to store nicknames or hydra head names.
Phase
: A container for data corresponding to a phase of the game, e.g., Day, Night, etc. Each game starts in a phase called "Pregame" to represent the fact that the first day/night is not in effect the instant a game thread is made. The Phase abstraction exists so that, for instance, if a mod wanted to create a Nightless setup, or a variant setup with two lynching phases (Morning and Afternoon) for each night, they could do that without a lot of hassle.
PhaseType
: A representation of an idealized "type" of phase. For example, Day 2 is a particular phase that is an instance of the idealized "Day" phase. The "new" method is used to make a Phase from a PhaseType.
Election
: An election is any instance of an outcome being voted on in a game, most commonly a lynch vote (but it could be, for instance, the Social Policy vote from Civilization Mafia or w/e a mod wants). Each election corresponds to a phase and contains Electors and Votes.
ElectionType
: A representation of an idealized "type" of election. For example, the Day 3 lynch vote is an Election, and the type "Lynch" is an ElectionType.
Elector
: Any entity capable of voting or receiving votes in an election. Usually corresponds to a player, but I've created a separate object for it because 1) sometimes a label needs to be capable of receiving votes, e.g., a social policy in Civ Mafia or a bundle of players in a Partition game; and 2) a player might receive a modifier that only applies for a particular election and not in general, e.g., having doublevote for the lynch vote, but not for the vote for a town leader.
Vote
: A vote cast by an Elector for another Elector. Can have a "power" assigned to indicate the strength of the vote; for instance, a doublevoter casts votes with a power of 2.

There's also a couple of abstract classes that are inherited all over the place:
Temporal
: Anything that inherits Temporal has a start and end post, and helper methods like active() and past() to make it easier for other code to do time comparisons.
Modifiable
: Contains the Modifier class; any object that inherits Modifiable gets a dict called "modifiers" and some helper methods to add and fetch keys from it. The idea of this is to encapsulate special behavior into little tags that can be passed in via a configuration JSON string. Modifiers are Temporal and anytime a modifier changes, the old value of the modifier sticks around in the modifiers dict. The only use of this right now that I'm a little torn on is to track a player being alive -- a player with the "alive" == True modifier is considered alive, otherwise they're not. I did this just in case some weirdo mod wanted to make it possible to revive players in a game.

Then, to manage the GameState, you've got a couple of other classes:
Game
: This is just intended to be an interface into a GameState so it's not a massive pain to tell it to do things. The externally-accessible methods to Game should all be pretty self-explanatory, user-facing stuff. Still a major work in progress.
Event
: This is the part where I should make explicit that this code is
not
intended to go scrape pages for votes, as I think Psyche's already doing an amazing job on that front. Instead, I use JSON strings called Events to manipulate the GameState. Ideally, these should be the only interfaces into a GameState capable of modifying its state -- everything else should look and not touch. Events are things that happen that change the GameState, such as a "death" event for a player dying, a "vote" or "unvote" event for votes, a "vote_count" event to record that a vote count has been posted to the thread (handy if mods want their vote counts to link back to old vote counts), a "deadline" event when deadlines are set, a "phase_change" event when the phase changes (e.g., Day to Night), etc. etc. Events are loaded into the Game object (currently just from a file) using the load_events() method and then are processed using the process_events() method. The Game object keeps a big ol' list of events that it runs through in chronological order to calculate the GameState. Currently, I have no way to allow events to be processed out of order, so if an event is received out of order, the GameState has to be reset and calculated from scratch.

Right now, I've got a bunch of admittedly-pretty-bad code lying around that grabs the setup data and events data from JSON files in the repository (in the respective subfolders). These are just big chunks of JSON that contain the setup data and a huge list of all events, respectively. For my tests thus far, I've had to read through game threads myself and manually write event JSON for each thing that happens -- for an example of that, I recently wrote out the setup and event data for Mini Theme 1974 by hand, and put in some code needed to generate vote counts for it in the Main.py file. You can get a sense of how the JSON is laid out from that. I think it's going to be important to standardize the JSON for setups, events, votes, etc. earlier rather than later so please let me know if you have any suggestions for this! I've actually never worked with JSON before this project so there are undoubtedly some things I'm doing weirdly.

Finally, there are things I'm calling
Components
which are just ways to pull data from a GameState and its members and represent them in a lovely text format -- stuff like vote counts, player lists, role PMs (not there yet though), etc. I think this is easily the least intuitive part of what I've written here, but I like how modular it is so I'll take a stab at documenting it in the next couple of days so it's clear what it's doing. The important thing to note is that when you generate a vote count through the Game object, you can pass a Style in which tells the component how to format stuff. In Main.py I've passed in a style called "Micc" which just replicates the style of vote count Micc was using for Mini Theme 1974, just as a proof-of-concept for how customizing output works. All of the files for each style are in the "styles" subdirectory. Basically, you have two types of file inside each style:

- "body" files: These define the overall structure of each component. The "vote_count.component.body" file (i.e., the body file for the vote_count component) is probably the easiest to visually parse. The Component code just plugs 'n' chugs data into the body using Python's .format() function. And, yes, components can contain other components, so for instance, in order to represent the actual 'vote count' meat of a each vote count, I have a "list" subcomponent of newline-separated "vote_count_vote" components, which is a composite of the votee's "player_label" component, plus the a "list" subcomponent of comma-delimited "player_label" subcomponents for each voter. It's kind of messy but I think it'll be worth it, as mods can make a global change in their Style directory to how, say, they want player names displayed, and it'll affect all "player_label" components used anywhere in their Style.
- "configs" files: These are parameters to be passed into each component when it's created to define some customizable behaviors. For example, in the "_default" style, the "players_list" component gets a parameter passed in so that replacements for each player are displayed, as this is common practice for OPs pretty much everywhere. If for some reason a mod didn't want to display replacements, they could flip it to False in their own Style file.

... phew, anyway, I hope that was informative. I don't know how much interest there will be from others when it comes to using this, but I hope at least anyone who's interested will offer pointers and suggestions. Despite wanting to move professionally in that direction, I am not a developer, so undoubtedly the folks here who do this stuff for a living will be shaking their heads at some of what I'm doing here. Feedback (yes, negative feedback too) is very welcome!

So, first TODO for me: I really like the .md readme thing you have going on for ModBot, Psyche, so I'm going to try to create one for my files to make it easier for people to tell what the heck is going on.

Also, yea or nay on having most classes in their own file? I'm not sure if that's standard in Python world but it's what I've been doing up until now.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #93 (isolation #5) » Fri May 11, 2018 7:17 pm

Post by GreenLiquid »

In post 89, yessiree wrote:@GreenLiquid, I skimmed over your code and think I got a good sense of what you're trying to do. The thing is a beast to say the least.

That said, I can't help but feel that we are approaching this with fundamentally different mindsets. I see that you are trying to record perfect history and store them in JSON files. Then you can generate whatever you need from these data. Whereas I'm only interested in data that are relevant to my needs, so most of the time it will be grabbing the most recent votes, generating a VC, and moving on.

Your approach is perfectly fine. I'm trying to think of what you'd need to automate the data generation process. You could write a crawler that monitors the thread and polls for every action that's worth collecting data for. Or you could write a program that starts scanning the thread only when you're asking something from it. You could keep track of the post number of where it left off last time it is ran though. Either way it's just something I wouldn't do, but if you could do this it would be pretty impressive, not to mention the data you collect would be invaluable.
I think in hindsight I went a little bit crazy, but my aim has been to make the module robust enough for full automation to be possible in the distant future, and also to allow for games with very unorthodox or voting rules or powers. Hopefully with some decent UI design it will be possible to hide most of the complexity from end users unless they want to go ham automating a weird setup -- I'd like to make it completely painless to automate a "normal" game (even if only the day phases at this point).

If I integrate with Psyche's module it should be doable to create a crawler to monitor the thread, parse posts / PMs for events, and send out prods automatically. The part that's proving challenging right now is coming up with a model for this process that's absolutely as hardened as possible against race conditions and ill effects from, say, accidentally parsing one event before another. I have a few ideas on this front but I've been feeling sick today so that might have to happen next week.

I'd also like to make role abilities modular in the way Psyche was suggesting earlier in this thread. That'd make it easier for mods to create 'composite' roles like Mafia 2-Shot Macho Roleblocker / Even-Night Neighborizer, since they'd just have to supply the labels with the assurance that the logic for the 'normal' version of these roles is already built into the module. This piece on its own is probably going to be a huge amount of work.

Has anyone ever formulated the logic behind NAR in a way that can be easily adapted to an application? I can already see that challenge looming over the horizon. :P
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #94 (isolation #6) » Fri May 11, 2018 7:41 pm

Post by GreenLiquid »

My road map currently looks something like this:

Phase I - Manual Vote Gathering (Modbot as Assistant):
- Create a UI to make it easier to transcribe events than writing JSON by hand. I might be able to make this a new page in the Web app Psyche just created. That said, I'm utter garbage at UI and Web coding so that's going to be a learning experience. :lol:
- Also create a UI to make it easier to create the setup JSON file.
- Add additional functionality to Components -- basically, go rummaging through the game archives for every kind of OP and vote count variant I can dig up and make sure my Component logic can handle it (or have a good reason why not).
- Review and reorganize the Election logic -- it's currently a bit of a mess and the logic for multiple votes per player hasn't even been tested yet. I'd like to make that more modular so I can test the pieces in isolation.
- Create documentation and add comments.
- In general, make sure my code can play nice with other people's modules, in case they'd like to make use of it!

Phase II - Automatic Vote Gathering (Modbot as Auto-VC-Generator)
- Hook into the DonBot class with an interface that can turn votes it counts into Events to be passed to the GameState object.
- Finalize strategy for integrating parsing of posts with the event processing code -- i.e., how to minimize race conditions.
- Add logic to automatically detect when a lynch is achieved and reject vote changes in twilight.
- Hook the Generator class into the posting functionality of DonBot (and integrate with the pagetopper too) so that vote counts can be posted as they are generated.
- Test and harden the system -- Mafia games are very easy to ruin with even a minor slip-up, so the moduleneeds to be ironclad and there need to be fail-safes carefully considered and built in, especially surrounding player roles and alignments.

Phase III - Full Automation of Normal Games (Modbot as Fully-Functional Mod)
- Implement a module for tracking night actions and calculating results using NAR (this is going to be a biggie).
- Expand parsing logic to PMs and PTs, especially the scum PT.
- A whole bunch of other challenges I haven't even thought of yet.

Phase ?? - Full Automation of a Varsoon Game (Modbot as Obscene Pipe Dream)
- Grovel before AI overlords.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #95 (isolation #7) » Fri May 11, 2018 7:47 pm

Post by GreenLiquid »

In post 91, Psyche wrote:speaking of which, for some reason making a proper votecounter tester that works as fast as what i made to support development of my own votecounter has turned out to be kinda difficult
i'm honestly sort of flabbergasted by its slowness; it seems to take 3 times as much time as my old thing, but the only change i've intentionally made was organizing the code into classes so that the tester could take a votecounter as an argument

i don't want to let that slow me down, so i'm gonna go ahead and take the votecounter I have now and add a demo app to the client-side app i released yesterday. For now but hopefully only for a while, my votecounter tester will remain a private tool to assist developing my own votecounter, rather than a public tool anyone can use to test their own stuff.

What votecounterdemo will do is take a threadurl, a playerlist, and a startpost and an endpost (expected to indicate the interval between the start of a phase and its ending), and extract an accurate votecount from that thread.

A full votecounter needs to do a bit more than that, as it needs to allow room for specifying vote-manipulating PRs, votecount formatting, and all kinds of game moderator preferences. It also needs to actually make posts, of course.

Anyway, think I'll have this demo done tonight or soon after.
I'm really looking forward to seeing the code for parsing posts for votes, as the level of D1 lynch accuracy you achieved with that thing is extremely impressive. In theory, if someone were using a ModBot to run their vote counts entirely, they could simply require players to format votes in an unambiguous way, but I like the added utility of the code being clever enough to figure out votes that don't adhere to a strict format. As you mentioned earlier, we could also use it to automatically collect full voting data from old games really quickly, which I'm sure our resident statisticians and number-crunchers in MD would adore. :)
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #117 (isolation #8) » Tue May 15, 2018 1:18 pm

Post by GreenLiquid »

If we ever get to the point of game automation, though, it'll crop up again when there's a need to send role PMs in a large-sized game. How does it work with a delay of 10s?

Also I've been... not working on this at all due to being sick over the weekend and the past couple of days. Once I improve I want to take the PM and autoposting code for a test spin and see what the logic to automate in the context of an actual game would look like.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #120 (isolation #9) » Tue May 15, 2018 2:11 pm

Post by GreenLiquid »

Does the result change if you push 13 PMs while the application is running, kill the application, then immediately restart it and send 1 additional PM?

If it turns out this is unavoidable for some reason, I guess we could include a step where the bot checks its outbox / sent box for the message to ensure it actually sent before proceeding to its next action, but that'd really slow things down.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #123 (isolation #10) » Tue May 15, 2018 2:16 pm

Post by GreenLiquid »

Right, sorry, I meant... is it possible to get it out of its state of not sending PMs by exiting and re-launching the application? Just to see if it's something within that session or not. Might help narrow down the cause.
Avatar courtesy of Chickadee! | GTKAL
User avatar
GreenLiquid
GreenLiquid
Mafia Scum
User avatar
User avatar
GreenLiquid
Mafia Scum
Mafia Scum
Posts: 1054
Joined: July 15, 2005

Post Post #129 (isolation #11) » Tue May 15, 2018 2:51 pm

Post by GreenLiquid »

Yeah, something like that. But Psyche might have an insight to allow us to avoid a trial-and-error approach altogether.
Avatar courtesy of Chickadee! | GTKAL
Post Reply

Return to “Mafia Discussion”