Python Forum
Randomizer script to provide options based on user and priority
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Randomizer script to provide options based on user and priority
#1
Ok, so i tend to get bored at home sometimes and always looking for fun projects to work on and some of them work with my daughter on as well..

So the family tends to play alot of board games.. and on the rare occasion that EVERYONE wants to go out to dinner its a task in itself, so this idea may serve both problems..

So here is what im trying to do for this project, as a family we have well over 40 or 50 board games and picking one that EVERYONE likes or wants to play is a pain sometime, so for testing purposes i want to try something out..

Id like to try and create some script that will take our list of games, our names and priority we each give to the games to help provide a specific game or maybe a short list of 2 or 3 games to pick from.

so basically the way i picture it working is 1 of 2 ways..

#1 fully automatic in that we run the script and it picks just 1 game from the entire list at random and we play that..

#2 we select who in the family is playing, since its not always the whole group, and based on the users selected and the priority they have given each game, the script will return a random game that ONLY those family members like maybe even have a flag that it returns just 1 game or 2 options..

Scenarios below:

Family1 - Monopoly - #2
Family1 - Uno - #1
Family1 - Candyland - #3
Family2 - Uno - #2
Family2 - Life - #1
Family2 - Connect 4 - #3
etc...
So on and so on for the setup..

Now lets say that only Family1 and Family5 are playing today.. i know that in this example 2 people can choose among themselves, but trying to keep this description as simple as i can think..

So they run the script and let it automatically pick 1 game that they both like.. and if they don't like that game, then they run it again..

Now lets say that Family1, Family2, Family5, Family7 and Family8 are wanting to play.. so they either let it pick one game among ONLY the games they each like OR the script returns a list of 2 or 3 games that as a whole, the would like..

Eventually ill create some fancy front end to handle the user interaction portion, but for now im just going for a proof of concept (for myself and my wife) since she thinks i over think and over engineer things..


So if i can create a list of users/game/priority is it possible to randomize the results based on selections or flags set before running the script?

If it is possible, what are things i should consider looking into? Would i create 3 separate lists or 1 list setup in a format that can be read easily to make those choices?
Reply
#2
Something like this?
import random
import collections
 
ALL_GAMES = [
    "Candyland",
    "COD",
    "Connect 4",
    "Life",
    "Monopoly",
    "Risk",
    "Skippo",
    "Uno",
    "Yahtzee"
]
 
class GamerMeta(type):
    """So we can do Gamer[player_name]"""
    def __getitem__(cls, name):
        return cls.Gamers[name]
 
class Gamer(metaclass=GamerMeta):
    Gamers = {}  # Dictionary of all players choices
 
    def __init__(self, name, games):
        """Create Gamer object.  Verify games choices"""
        for game in games:
            if game not in ALL_GAMES:
                raise ValueError(f"{game} is not in our collection")
        self.name = name
        self.games = games
        self.Gamers[name] = self  # Add gamer to Gamers list
 
    def votes(self):
        """Get weighted game selections for this Gamer"""
        if self.games == ALL_GAMES:
            return ALL_GAMES  # Game catalog is not weighted
 
        # Weight gamer's choices.  First choice gets 5 votes, second 4 votes and so on.
        vote = []
        for game, weight in zip(self.games, [5, 4, 3, 2, 1]):
            vote += [game] * weight
        return vote

    @classmethod
    def cast_ballots(cls, gamers):
        """Collect votes from gamers"""
        ballots = []
        for gamer in gamers:
            ballots += cls.Gamers[gamer].votes()
        return ballots
 
    def __repr__(self):
        """Get string with info about this gamer"""
        return f"{self.name} games = {self.games}"

def random_choice(*gamers):
    """Randomly choose from "votes" for all of tonights gamers"""
    return random.choice(Gamer.cast_ballots(gamers))
 
def weighted_choice(*gamers):
    """
    Select favorite game as voted on by gamers.  If multiple games tie
    as the favorite, randomly select from those games
    """
    ballots = collections.Counter(Gamer.cast_ballots(gamers)).most_common()
    return random.choice([game[0] for game in ballots if game[1] == ballots[0][1]])
 
# Add gamers to the "database"
Gamer("Catalog", ALL_GAMES)
Gamer("Dad", ("Uno", "Monopoly", "Candyland")),
Gamer("Mom", ("Life", "Uno", "Connect 4")),
Gamer("Thing 1", ("COD", "Monopoly", "Skippo", "Risk", "Uno")),
Gamer("Thing 2", ("Yahtzee", "Skippo", "Uno", "Life"))
 
# Try some random combinations to see how it works.
print("Taking a chance")
print("Tonight we play", random_choice("Catalog"))
print("Tonight we play", random_choice("Thing 1", "Thing 2"))
print("Tonight we play", random_choice("Mom", "Dad"))
print("Tonight we play", random_choice("Mom", "Dad", "Thing 1", "Thing 2"))
print("Tonight we play", random_choice("Mom", "Thing 1", "Catalog"))
 
# Try some weighted combinations to see how it works.
print("\nStrict democracy in action")
print("Tonight we play", weighted_choice("Catalog"))
print("Tonight we play", weighted_choice("Thing 1", "Thing 2"))
print("Tonight we play", weighted_choice("Mom", "Dad"))
print("Tonight we play", random_choice("Mom", "Dad", "Thing 1", "Thing 2"))
print("Tonight we play", weighted_choice("Mom", "Thing 1", "Catalog"))
This program can randomly selects a game from "weighted" player selections or it can select the game with the most votes. Weighting and voting are done by giving a player's first choice 5 votes, their second choice 4 votes and so on. Randomly selecting a game from the weighted has the highest probability of picking the game with the most number of votes, but being random can also pick the game with the least.
Reply
#3
ok, so looking at this code, had a question on the weighting of the games..

# Add gamers to the "database"
Gamer("Catalog", ALL_GAMES)
Gamer("Dad", ("Uno", "Monopoly", "Candyland")),
Gamer("Mom", ("Life", "Uno", "Connect 4")),
Gamer("Thing 1", ("COD", "Monopoly", "Skippo", "Risk", "Uno")),
Gamer("Thing 2", ("Yahtzee", "Skippo", "Uno", "Life"))
When adding the games in this fashion, would i place them in the weighting order here or how is that value assigned to the specific games?
Reply
#4
In my code the first game is the first choice. The first game gets 5 votes, the second 4 votes and so on. Games after 5 are ignored because there are only 5 weights. I think that easier than forcing you to give a game name and a weight. But if you wanted a different weighting scheme you could save games as a dictionary.
Gamer("Dad", {"Uno":3, "Monopoly":5, "Candyland:4"}),
Doing it that way you probably want to add code to prevent letting somebody give a weight of 10000, or giving 5 to all of their choices or having a dozen choices. Easier to just do a list in order of of favor and cut off the game choices after 5.
Reply
#5
lol yea i think the top 5 rating options works best, no need to try and have everyone in the family rate all 50 games..

sweet, ill be playing around with this tonight and this weekend, thank you..
Reply
#6
It is easy to change the weighting. Just change the values in the list:
for game, weight in zip(self.games, [5, 4, 3, 2, 1]):
If you wanted to allow up to 10 favorites:
for game, weight in zip(self.games, [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]):
If you wanted a "flatter" weighting. Here the least favorite has 60% the weight of the favorite instead of 20%.
for game, weight in zip(self.games, [10, 9, 8, 7, 6]):
Reply
#7
Going to be working on this today, so one last question..
Say we do have the 40 or 50 games, not sure the true number yet, but if i go with the top 5 for each person, how does setting up the lists work?

I mean yes i want a way to have it pick from our most favorite games, but at the same time, we will eventually like to play some of the other games occasionally. So just want to make sure that the weighting doesnt eliminate any of the other games from ever being selected.
Reply
#8
That is what Catalog does in my example. Catalog is a special gamer that likes all the games equally. If you want to be open to playing games not on anyones list you just add Catalog to your list of gamers for the night. If you want to randomly select a game with no bias have Catalog be the only gamer.

And top 5 is only what I chose for the example. If you want more, just change the weighting as mentioned in my previous post.
Reply
#9
very cool... im going to load in all the games we currently have and play around with this..
thank you so much..

So just to make sure, for my testing this morning, IF its only me and my daughter that want to play a game, ill run one of these based on what we want to play? Correct?

And one of these if we want to stick to our favorites
print("Tonight we play", random_choice("Dad", "Thing 1"))
Or

print("Tonight we play", weighted_choice("Dad", "Thing 1"))
Or these if we dont care if its one of our favorites...

print("Tonight we play", weighted_choice("Catalog"))
Or

print("Tonight we play", random_choice("Catalog"))
Reply
#10
With Catalog only, weighted and random are the same because all games have the same number of votes.

You can also do dad, thing 1 and catalog to pick a game that is more likely on your lists, but still have the possibility of picking a game that is not.

With a slight modification you can see the voting:
    @classmethod
    def cast_ballots(cls, gamers):
        """Collect votes from gamers"""
        ballots = []
        for gamer in gamers:
            ballots += cls.Gamers[gamer].votes()

        print("Weighted choices for", ", ".join(gamers))
        for game in collections.Counter(ballots).most_common()[:5]:
            print(f"{game[0]:10} {100*game[1] / len(ballots):>6.2f}%")

        return ballots
Output:
Weighted choices for Mom, Dad, Thing 1, Thing 2 Uno 24.53% Monopoly 15.09% Life 13.21% Skippo 13.21% COD 9.43%
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  output provide the filename along with the input file processed. arjunaram 1 945 Apr-13-2023, 08:15 PM
Last Post: menator01
  Priority Queue with update functionality PythonNewbee 4 1,956 Dec-29-2022, 12:13 PM
Last Post: Yoriz
  Performance options for sys.stdout.writelines dgrunwal 11 3,151 Aug-23-2022, 10:32 PM
Last Post: Pedroski55
  Exit function from nested function based on user input Turtle 5 2,931 Oct-10-2021, 12:55 AM
Last Post: Turtle
  Automatic user/password entry on prompt by bash script PBOX_XS4_2001 3 2,793 May-18-2021, 06:42 PM
Last Post: Skaperen
  Creating a calculation based on user entry blakefindlay 2 2,019 Jan-26-2021, 06:21 PM
Last Post: blakefindlay
  [SOLVED] Requiring help running an old Python script (non Python savvy user) Miletkir 13 5,453 Jan-16-2021, 10:20 PM
Last Post: Miletkir
  Loop back through loop based on user input, keeping previous changes loop made? hbkpancakes 2 2,958 Nov-21-2020, 02:35 AM
Last Post: hbkpancakes
  Can argparse support undocumented options? pjfarley3 3 2,224 Aug-14-2020, 06:13 AM
Last Post: pjfarley3
  Help with options raiden 1 1,936 Aug-30-2019, 12:57 AM
Last Post: scidam

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020