ekiefl / pooltool

A sandbox billiards game that emphasizes realistic physics
https://ekiefl.github.io/projects/pooltool/
Apache License 2.0
120 stars 30 forks source link

Snooker game + modular game modes #99

Closed ekiefl closed 10 months ago

ekiefl commented 11 months ago

Hi @kerby2000,

I took a look at your changes in https://github.com/ekiefl/pooltool/pull/94. First of all, well done. I'm impressed at your ability to start modifying a foreign codebase.

I noticed most of the things you got snagged on are due to a lack of "pluggability" in the codebase. To address this, I've branched off your code and have been working on generalizing things. You can track that work in progress in this branch.

If you want to continue making changes and contributing to this side project, I would suggest you become a collaborator. Then, you would have permission to commit directly to branches in ekiefl/pooltool, rather than kerby2000/pooltool. Let me know if you want this, and I'll send you an invite.

kerby2000 commented 11 months ago

Hi Evan,

Last few weeks I had very little time to work on your project. Mostly busy with final touches of my new house (garden stuff, shed, ect :) From next week onwards I will have more time.

Last time I wrote to you I was busy experimenting with a cue tracking device. Basically same as this one QMD Stroke Analyzer for Billiards | QMD (cue-md.com) http://www.cue-md.com/ At that time I thought that project was dead as their product was not available and was not maintained for a few years and I decided to make my own device. Later on I received the email from the developer that he was out of parts and was waiting for a new batch. Now I can get it for 179$ but I find it rather expensive :)

I thought it might be a valuable addition to your code as well to model cue behaviour and introduce some sort of randomness into the shot. Later on we can hook the data from a device to read velocity and straightness of the que.

By now I assembled HW consisting of:

I'm sending yaw/pitch/roll angles + raw accelerations and gyro readings + timestamp.

[image: image.png]

Now the more difficult part is to make sense of that :)

From what I read in the manual to QMD3 device the que movement looks like this. That is where I stopped. [image: image.png]

Sure I would like to be a collaborator. How can I apply for that? :)

Kind regards, Sergey Lukin +31644533870

On Sun, Oct 8, 2023 at 1:40 PM Evan Kiefl @.***> wrote:

Hi @kerby2000 https://github.com/kerby2000,

I took a look at your changes in #94 https://github.com/ekiefl/pooltool/pull/94. First of all, well done. I'm impressed at your ability to start modifying a foreign codebase.

I noticed most of the things you got snagged on are due to a lack of "pluggability" in the codebase. To address this, I've branched off your code and have been working on generalizing things. You can track that work in progress in this branch.

If you want to continue making changes and contributing to this side project, I would suggest you become a collaborator. Then, you would have permission to commit directly to branches in ekiefl/pooltool, rather than kerby2000/pooltool. Let me know if you want this, and I'll send you an invite.

You can view, comment on, or merge this pull request online at:

https://github.com/ekiefl/pooltool/pull/99 Commit Summary

File Changes

(104 files https://github.com/ekiefl/pooltool/pull/99/files)

Patch Links:

— Reply to this email directly, view it on GitHub https://github.com/ekiefl/pooltool/pull/99, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADPKHVUP46ZIY3TCLVQ77LX6KGKLAVCNFSM6AAAAAA5XUBGMSVHI2DSMVQWIX3LMV43ASLTON2WKOZRHEZTCNZXGI3DOMQ . You are receiving this because you were mentioned.Message ID: @.***>

ekiefl commented 11 months ago

Wow, that is so cool. Amazing how you pieced that all together. How do imagine pooltool might synergize with this?

I'll invite you for a collaboratorship. Here are the instructions you should follow (chatgpt generated) for leaving your fork behind and beginning to work with the main repo.

Transitioning from Fork to Direct Collaboration on the pooltool Repository

  1. Accept the Invitation: First, accept the invitation sent to you to join the pooltool repository as a collaborator.

  2. Clone the pooltool Repository: If you've been working from a forked version, it's time to clone the main pooltool repository to your local machine:

    git clone https://github.com/ekiefl/pooltool.git
  3. Navigate to the Cloned Directory: Change your directory to the newly cloned repository:

    cd pooltool
  4. Set Up Remotes: If you previously had the pooltool repository set up as an 'upstream' remote from your fork, you can skip this step. Otherwise, it's good practice (though technically unnecessary since you're working directly with the original repo now) to add the pooltool repository as a remote:

    git remote add upstream https://github.com/ekiefl/pooltool.git
  5. Fetch and Merge Changes from Upstream: Always ensure you have the latest changes before starting any new work:

    git fetch upstream
    git merge upstream/main
  6. Create a New Branch for Your Work: Instead of working directly on the main branch, create a new branch for your changes:

    git checkout -b your-feature-branch
  7. Push and Create Pull Requests: After making your changes, push them to the pooltool repository:

    git push origin your-feature-branch

    Then, you can create a Pull Request (PR) using the GitHub web interface.

  8. Stay Synced: Since you're directly collaborating now, ensure you regularly pull the latest changes from the main branch before starting new work:

    git pull origin main

To contribute specifically to this branch, instead of following step 6 (creating a new branch) you would just go git checkout snooker-game.

kerby2000 commented 11 months ago

Hi Evan,

I was already using your git so for me it was just a matter of switching. In VS Code the only thing I needed to do is to select - snooker-game from the bottom of the screen [image: image.png]

I tried running again. The game starts and I can pot several balls but as soon as I pot a "wrong ball". Game crashes with exceptions. I'm sure it is a bug in logic but I don't understand how to debug it. Could you have a look? [image: image.png]

Second question I have is about matching the snooker table model with the blender file. Currently they don't match.

You described that for your table here: Pooltool just underwent a massive graphics overhaul – Evan Kiefl (ekiefl.github.io) https://ekiefl.github.io/2021/10/26/graphics/ But that info is rather brief for me. I have many questions on how to do that matching :) How can I enable grey lines in the model? Is there a way to read the same parameters from Blender? Is there logging to see what dimension is in the blender file and what is in the model? Or can I only tell it visually?

Kind regards, Sergey

On Mon, Oct 9, 2023 at 3:23 PM Evan Kiefl @.***> wrote:

Wow, that is so cool. Amazing how you pieced that all together. How do imagine pooltool might synergize with this?

I'll invite you for a collaboratorship. Here are the instructions you should follow (chatgpt generated) for leaving your fork behind and beginning to work with the main repo.

Transitioning from Fork to Direct Collaboration on the pooltool Repository

1.

Accept the Invitation: First, accept the invitation sent to you to join the pooltool repository as a collaborator. 2.

Clone the pooltool Repository: If you've been working from a forked version, it's time to clone the main pooltool repository to your local machine:

git clone https://github.com/ekiefl/pooltool.git

3.

Navigate to the Cloned Directory: Change your directory to the newly cloned repository:

cd pooltool

4.

Set Up Remotes: If you previously had the pooltool repository set up as an 'upstream' remote from your fork, you can skip this step. Otherwise, it's good practice (though technically unnecessary since you're working directly with the original repo now) to add the pooltool repository as a remote:

git remote add upstream https://github.com/ekiefl/pooltool.git

5.

Fetch and Merge Changes from Upstream: Always ensure you have the latest changes before starting any new work:

git fetch upstream git merge upstream/main

6.

Create a New Branch for Your Work: Instead of working directly on the main branch, create a new branch for your changes:

git checkout -b your-feature-branch

7.

Push and Create Pull Requests: After making your changes, push them to the pooltool repository:

git push origin your-feature-branch

Then, you can create a Pull Request (PR) using the GitHub web interface. 8.

Stay Synced: Since you're directly collaborating now, ensure you regularly pull the latest changes from the main branch before starting new work:

git pull origin main

To contribute specifically to this branch, instead of following step 6 (creating a new branch) you would just go git checkout snooker-game.

— Reply to this email directly, view it on GitHub https://github.com/ekiefl/pooltool/pull/99#issuecomment-1753007056, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADPKHU6R6DBNQSKVO4ALXTX6P3E5AVCNFSM6AAAAAA5XUBGMSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONJTGAYDOMBVGY . You are receiving this because you were mentioned.Message ID: @.***>

ekiefl commented 11 months ago

I was already using your git so for me it was just a matter of switching. In VS Code the only thing I needed to do is to select - snooker-game from the bottom of the screen

Can you try and make a small change, commit it, and then push it? If your change can be seen as a commit within this PR page, it worked. Otherwise, you need to clone my repo as per the instructions.

I tried running again. The game starts and I can pot several balls but as soon as I pot a "wrong ball". Game crashes with exceptions. I'm sure it is a bug in logic but I don't understand how to debug it. Could you have a look?

Yeah, I can take a look. Right now, all the rulesets are broken.

How can I enable grey lines in the model?

I added it (7de5d2b) so if you pull you should see how they overlap.

Is there a way to read the same parameters from Blender?

Unfortunately, I think you have to make the measurements from within Blender, but I don't know how to do that efficiently.

ekiefl commented 10 months ago

Summary

Alright, I just merged this branch.

Huge credit to @kerby2000 for creating all the snooker models, creating most of the datatypes for snooker, taking a first pass at the snooker rules, and identifying where the current API was unable to accommodate a different cue sport due to a lack of generalization. Thanks to this, I was able to clean up the structure of the codebase to accommodate snooker. And next time someone adds a game mode, it will be much simpler.

With this PR there are lots of changes, but the TL;DR of it all is that eight ball, nine ball, three cushion, and snooker are all valid game modes and all the rules have been (mostly) properly implemented.

There is still no menu to swap between the game modes, but at least for developers, one can switch game modes by changing a single line in pooltool/ani/animate.py in the create_system(...) method of Game:

    def create_system(self):
        """Create the multisystem and game objects

        FIXME This is where menu options for game type and further specifications should
        plug into.
        """
        # Change this line to change the game played.
        # Pick from {NINEBALL, EIGHTBALL, THREECUSHION, SNOOKER, SANDBOX}
        game_type = GameType.NINEBALL
        (...)

In the future we'll have to create a menu system that modifies this variable, but for now, I consider it a huge success that a single Enum controls which balls, ball parameters, ball layouts, ball models, table specifications, table models, and game are active.

List of changes

Here's a list of all the features added in this PR

1. Fonts have been consolidated in pooltool/ani/fonts/__init__.py

2. Panda's OnscreenText has been wrapped with CustomOnscreenText, which accepts font names present in the repo rather than font objects

3. GUI message log has been refactored and is now bug-free

4. Help popup instructions have been rewritten for accuracy

5. Aim, View, and Stroke modes respect the constraints of the shot

Now taking a shot is now only possible if the game shot constraints are satisfied (such as the shot requiring calling a ball)

6. Event filtering has a new API that allows chaining

Instead of nesting function calls, a chaining syntax has been introduced, which is much cleaner and readable.

For simple filtering, one has access to the following functions:

filter_time(events, t=4.4, after=True)
filter_type(events, EventType.BALL_BALL)
filter_ball(events, "cue")

Before, filtering events by all of these looked like:

filtered_events = filter_ball(filter_type(filter_time(events, t=4.4, after=True), EventType.BALL_BALL), "cue")

Now, one can filter like this:

filtered_events = filter_events(
    events,
    by_time(t=4.4, after=True),
    by_ball("cue"),
    by_type(EventType.BALL_BALL),
)

pooltool/events/filter.py has corresponding unit tests.

7. An easy to use API for ball layouts

Creating arbitrary table arrangements, such as the starting ball positions for snooker, nine ball, eight ball, and three cushion billiards, is now unified under an API found in pooltool/game/layouts.py.

The core functionality is generate_layout, which looks like this:

def generate_layout(
    blueprint: List[BallPos],
    table: Table,
    ballset: Optional[BallSet] = None,
    ball_params: Optional[BallParams] = None,
    spacing_factor: float = 1e-3,
    seed: Optional[int] = None,
) -> Balls:
    """Generate Ball objects based on a given blueprint and table dimensions.

    The function calculates the absolute position of each ball on the table using the
    translations provided in the blueprint relative to table anchors. It then randomly
    assigns ball IDs to each position, ensuring no ball ID is used more than once.

    Args:
        blueprint:
            A list of ball positions represented as BallPos objects, which
            describe their location relative to table anchors or other
            positions.
        table:
            A Table. This must exist so the rack can be created with respect to
            the table's dimensions.
        ball_params:
            A BallParams object, which all balls will be created with. This
            contains info like ball radius.
        spacing_factor:
            FIXME Get ChatGPT to explain this.
        seed:
            Set a seed for reproducibility. That's because getting a rack
            involves two random procedures. First, some ball positions can be
            satisfied with many different ball IDs. For example, in 9 ball, only
            the 1 ball and 9 ball are predetermined, the positions of the other
            balls are random. The second source of randomnness is from
            spacing_factor.

    Returns:
        balls:
            A dictionary mapping ball IDs to their respective Ball objects, with
            their absolute positions on the table.

    Notes:
    - The table dimensions are normalized such that the bottom-left corner is
      (0.0, 0.0) and the top-right corner is (1.0, 1.0).
    """

The primary information is held in a list of ball positions, passed as the first argument. One can create BallPos objects with respect to normalized table coordinates, or with respect to other balls, or a combination of the two.

To make it easier to create racks, there is ball_cluster_blueprint, which will create a list of BallPos objects from a seed BallPos, which must be defined with respect to normalized table coordinates (not with respect to other another BallPos), and a series of quantized jumps along with the ball IDs that can exist at the jumped position.

As an example that uses this functionality, see how easily an eight ball layout can be constructed:

def get_eight_ball_rack(*args, ballset: Optional[BallSet] = None, **kwargs) -> Balls:
    if ballset is None:
        ballset = DEFAULT_STANDARD_BALLSET

    stripes = {"9", "10", "11", "12", "13", "14", "15"}
    solids = {"1", "2", "3", "4", "5", "6", "7"}

    blueprint = ball_cluster_blueprint(
        seed=BallPos([], (0.5, 0.77), solids),
        jump_sequence=[
            # row 2
            (Jump.UPLEFT(), stripes),
            (Jump.RIGHT(), solids),
            # row 3
            (Jump.UPRIGHT(), stripes),
            (Jump.LEFT(), {"8"}),
            (Jump.LEFT(), solids),
            # row 4
            (Jump.UPLEFT(), stripes),
            (Jump.RIGHT(), solids),
            (Jump.RIGHT(), stripes),
            (Jump.RIGHT(), solids),
            # row 5
            (Jump.UPRIGHT(), stripes),
            (Jump.LEFT(), solids),
            (Jump.LEFT(), stripes),
            (Jump.LEFT(), stripes),
            (Jump.LEFT(), solids),
        ],
    )

    cue = BallPos([], (0.6, 0.23), {"cue"})
    blueprint += [cue]

    return generate_layout(blueprint, *args, ballset=ballset, **kwargs)

8. Ruleset baseclass changes

Ruleset used to require many abstract methods that inheriting classes were required to implement, and Ruleset would then call these methods sequentially in an order that was determined by the base class. This is highly restrictive, and doesn't capture bespoke and unique rulesets.

Instead, Ruleset implements a more hands off approach and mostly just manages whose turn it is, and two main objects: ShotInfo and ShotConstraints. Inheriting classes are now expected to build ShotInfo and ShotConstraints objects for processing the current shot and creating expectations and rules for the upcoming shot. This is a way better approach that creates ruleset flexibility, which is a good idea given the large diversity of rulesets (three cushion, eight ball, nine ball, snooker, to name a few).

9. Games are now properly implemented

The following games are now implemented with potential caveats:

Eight ball

Nine ball

Three cushion

Snooker

Sandbox

9. Balls and tables have prebuilts depending on game mode

10. Erratic strokes no longer cause game-crashing index error

kerby2000 commented 10 months ago

Hi @ekiefl

Wow, That is hell of a lot changes :) Well done.

Snooker table model I found on the BlenderSwap website https://blendswap.com/blend/30017

My Blender skills are terrible to get it fixed. I tried but could not make much progress. I could ask the original developer of the model to help with that. I hope that would be much quicker than me figuring out how to do that.

ekiefl commented 10 months ago

That's a good idea. After seeing their table brought to life within a video game, they may become enthusiastic about modeling more tables! I'll reach out to them.